From 2bd1b390346db5d05f24ecf6b8653f56d029ea32 Mon Sep 17 00:00:00 2001 From: jameshalgren <53343824+jameshalgren@users.noreply.github.com> Date: Thu, 3 Dec 2020 09:30:15 -0600 Subject: [PATCH 01/54] Include short time step arg in compute_network call --- src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 81986231e..9bbe6e897 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -289,6 +289,7 @@ def main(): param_df_sub.values, qlat_sub.values, q0_sub.values, + assume_short_ts, ) ) results = parallel(jobs) @@ -312,6 +313,7 @@ def main(): param_df_sub.values, qlat_sub.values, q0_sub.values, + assume_short_ts, ) ) From 6a4e1d3ad5569fb59af6917c4118e1eee8091199 Mon Sep 17 00:00:00 2001 From: jhrehanoaa <59839548+jhrehanoaa@users.noreply.github.com> Date: Thu, 3 Dec 2020 09:53:25 -0600 Subject: [PATCH 02/54] qlat read and minor change to install file (#176) * qlat read and minor change to install file * added instructions * added qlat hookup * moved qlat section and removed custom input file * resized dataframe, but still receiving mismatch error * blackened file * constant qlat and qlat input folder working args --- install.md | 11 +- src/python_framework_v02/troute/nhd_io.py | 2 +- .../compute_nhd_routing_SingleSeg_v02.py | 136 ++++++++++++++++-- 3 files changed, 132 insertions(+), 17 deletions(-) diff --git a/install.md b/install.md index 461549b17..3432c40aa 100644 --- a/install.md +++ b/install.md @@ -2,18 +2,21 @@ This file provides more detailed instructions on how to install, configure, and get the project running. Summary instructions are provided in readme.md. -#Step 1 - Clone the repository +#Step 1 -- Activate a python environment on your local machine + +#Step 2 - Clone the repository git clone git@github.com:NOAA-OWP/t-route.git -#Step 2 - Compile necessary files +#Step 3 - Compile necessary files cd t-route/src/python_routing_v02/ + ./compiler.sh -#Step 3 - Run the model +#Step 4 - Run the model python3 compute_nhd_routing_SingleSeg_v02.py -#Step 3 - Alternative v01 model (defaults can be overwritten with custom input yaml) +#Step 4 - Alternative v01 model (defaults can be overwritten with custom input yaml) cd .. cd python_routing_v01 python3 compute_nhd_routing_SingleSeg_v01.py diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index 93289623d..7d50a4003 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -42,7 +42,7 @@ def read_mask(path, layer_string=None): def read_custom_input(custom_input_file): if custom_input_file[-4:] == "yaml": with open(custom_input_file) as custom_file: - data = yaml.safe_load(custom_file) + data = yaml.load(custom_file, Loader=yaml.SafeLoader) else: with open(custom_input_file) as custom_file: data = json.load(custom_file) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 9bbe6e897..8f8c5d496 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -17,6 +17,7 @@ import numpy as np import argparse import pathlib +import glob import pandas as pd from functools import partial from joblib import delayed, Parallel @@ -122,8 +123,56 @@ def _handle_args(): dest="supernetwork", default="Pocono_TEST1", ) + ql_arg_group = parser.add_mutually_exclusive_group() + ql_arg_group.add_argument( + "--qlc", + "--constant_qlateral", + help="Constant qlateral to apply to all time steps at all segments", + dest="qlat_const", + type=float, + default=10, + ) + ql_arg_group.add_argument( + "--qlf", + "--single_file_qlateral", + help="QLaterals arranged with segment IDs as rows and timesteps as columns in a single .csv", + dest="qlat_input_file", + ) + ql_arg_group.add_argument( + "--qlw", + "--ql_wrf_hydro_folder", + help="QLaterals in separate netcdf files as found in standard WRF-Hydro output", + dest="qlat_input_folder", + ) + ql_arg_group.add_argument( + "--qlic", + "--qlat_file_index_col", + help="QLateral index column number", + dest="qlat_file_index_col", + default="feature_id", + ) + ql_arg_group.add_argument( + "--qlvc", + "--qlat_file_value_col", + help="QLateral value column number", + dest="qlat_file_value_col", + default="q_lateral", + ) + parser.add_argument( + "--qlat_file_pattern_filter", + help="Provide a globbing pattern to identify files in the Wrf-Hydro qlateral output file folder", + dest="qlat_file_pattern_filter", + default="q_lateral", + ) parser.add_argument("--ql", help="QLat input data", dest="ql", default=None) - + # TODO: uncomment custominput file + # supernetwork_arg_group = parser.add_mutually_exclusive_group() + # supernetwork_arg_group.add_argument( + # "-f", + # "--custom-input-file", + # dest="custom_input_file", + # help="OR... please enter the path of a .yaml or .json file containing a custom supernetwork information. See for example test/input/yaml/CustomInput.yaml and test/input/json/CustomInput.json.", + # ) return parser.parse_args() @@ -167,10 +216,40 @@ def main(): break_network_at_waterbodies = args.break_network_at_waterbodies csv_output_folder = args.csv_output_folder assume_short_ts = args.assume_short_ts - + # TODO: uncomment custominput file + # custom_input_file = args.custom_input_file test_folder = pathlib.Path(root, "test") geo_input_folder = test_folder.joinpath("input", "geo") + # TODO: uncomment custominput file + # if custom_input_file: + # ( + # supernetwork_parameters, + # waterbody_parameters, + # forcing_parameters, + # restart_parameters, + # output_parameters, + # run_parameters, + # ) = nhd_io.read_custom_input(custom_input_file) + + # qlat_const = forcing_parameters.get("qlat_const", None) + # qlat_input_file = forcing_parameters.get("qlat_input_file", None) + # qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) + # qlat_file_pattern_filter = forcing_parameters.get( + # "qlat_file_pattern_filter", None + # ) + # qlat_file_index_col = forcing_parameters.get("qlat_file_index_col", None) + # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) + # else: + + # TODO: uncomment custominput file + qlat_const = float(args.qlat_const) + qlat_input_folder = args.qlat_input_folder + qlat_input_file = args.qlat_input_file + qlat_file_pattern_filter = args.qlat_file_pattern_filter + qlat_file_index_col = args.qlat_file_index_col + qlat_file_value_col = args.qlat_file_value_col + # print(forcing_parameters,qlat_const) # TODO: Make these commandline args """##NHD Subset (Brazos/Lower Colorado)""" # supernetwork = 'Brazos_LowerColorado_Named_Streams' @@ -212,22 +291,17 @@ def main(): param_df = param_df.sort_index() param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) - if args.ql: - qlats = nhd_io.read_qlat(args.ql) - else: - qlats = constant_qlats(param_df, nts, 10.0) + connections = nhd_network.extract_connections(param_df, cols["downstream"]) + wbodies = nhd_network.extract_waterbodies( + param_df, cols["waterbody"], network_data["waterbody_null_code"] + ) # initial conditions, assume to be zero - # TO DO: Allow optional reading of initial conditions from WRF + # TODO: Allow optional reading of initial conditions from WRF q0 = pd.DataFrame( 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" ) - connections = nhd_network.extract_connections(param_df, cols["downstream"]) - wbodies = nhd_network.extract_waterbodies( - param_df, cols["waterbody"], network_data["waterbody_null_code"] - ) - if verbose: print("supernetwork connections set complete") if showtiming: @@ -260,7 +334,45 @@ def main(): # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] + # STEP 5: Read (or set) QLateral Inputs + if showtiming: + start_time = time.time() + if verbose: + print("creating qlateral array ...") + + if qlat_input_folder: + qlat_files = glob.glob(qlat_input_folder + qlat_file_pattern_filter) + qlat_df = nhd_io.get_ql_from_wrf_hydro( + qlat_files=qlat_files, + index_col=qlat_file_index_col, + value_col=qlat_file_value_col, + ) + + qlat_df = qlat_df[qlat_df.index.isin(connections.keys())] + df_length = len(qlat_df.columns) + + for x in range(df_length, 144): + qlat_df[str(x)] = 0 + qlat_df = qlat_df.astype("float32") + + elif qlat_input_file: + qlat_df = nhd_io.get_ql_from_csv(qlat_input_file) + + else: + qlat_df = pd.DataFrame( + qlat_const, index=connections.keys(), columns=range(nts), dtype="float32", + ) + + qlats = qlat_df + + if verbose: + print("qlateral array complete") + if showtiming: + print("... in %s seconds." % (time.time() - start_time)) + start_time = time.time() + parallel_compute_method = args.parallel_compute_method + cpu_pool = args.cpu_pool compute_method = args.compute_method From 0a5f4af84e1d8358174452a3aa7279ede29f4019 Mon Sep 17 00:00:00 2001 From: donaldwj Date: Mon, 7 Dec 2020 13:54:17 -0600 Subject: [PATCH 03/54] Update ngen script to work with new hydro fabric (#169) * Debuging v1. * add missing files. * Add missing file. * Removing files deleted locally from git. * updating usage command. --- src/external_connections/next_gen_io.py | 3 +- src/external_connections/next_gen_network.py | 111 +++- .../{cat-87_output.csv => cat-58_output.csv} | 0 .../{cat-88_output.csv => cat-59_output.csv} | 0 .../{cat-89_output.csv => cat-60_output.csv} | 0 .../{cat-92_output.csv => cat-63_output.csv} | 0 test/input/next_gen/crosswalk.json | 539 ++++++++++++++++++ test/input/next_gen/flowpath_data.geojson | 137 +++++ 8 files changed, 758 insertions(+), 32 deletions(-) rename test/input/next_gen/{cat-87_output.csv => cat-58_output.csv} (100%) rename test/input/next_gen/{cat-88_output.csv => cat-59_output.csv} (100%) rename test/input/next_gen/{cat-89_output.csv => cat-60_output.csv} (100%) rename test/input/next_gen/{cat-92_output.csv => cat-63_output.csv} (100%) create mode 100644 test/input/next_gen/crosswalk.json create mode 100644 test/input/next_gen/flowpath_data.geojson diff --git a/src/external_connections/next_gen_io.py b/src/external_connections/next_gen_io.py index 0a7b51a98..d00541e5b 100644 --- a/src/external_connections/next_gen_io.py +++ b/src/external_connections/next_gen_io.py @@ -24,7 +24,8 @@ def read_catchment_lateral_flows(path): catchment_id = int(file_name_str_list[0][4 :]) catchment_id_list.append(catchment_id) # Read the second column of a csv file and return a series. The index will be an autoincrementing range. - catchment_qlats = pd.read_csv(os.path.join(path, file_name), names=[catchment_id], usecols=[1], squeeze=True) + catchment_qlats = pd.read_csv(os.path.join(path, file_name), usecols=['Flow'], squeeze=True) + catchment_qlats.rename(catchment_id, inplace=True) ql.append(catchment_qlats) qlats = pd.concat(ql, axis='columns').T diff --git a/src/external_connections/next_gen_network.py b/src/external_connections/next_gen_network.py index efadac36f..ebc9fb6d7 100644 --- a/src/external_connections/next_gen_network.py +++ b/src/external_connections/next_gen_network.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # coding: utf-8 -# example usage: python next_gen_network.py -n sugar_creek_waterbody_data_subset.geojson +# example usage: python next_gen_network.py -n ./data/coarse/flowpath_data.geojson -s cat-71 cat-42 cat-34 cat-26 cat-68 cat-67 cat-27 cat-52 -i ./data """This program reads in and modifies lateral flows from the next generation water modeling framework and feeds them into mc_reach.compute_network. This program presupposes that the mc_reach has been compiled prior to running.""" @@ -14,6 +14,7 @@ from functools import partial from itertools import chain, islice import next_gen_io +import json def _handle_args(): parser = argparse.ArgumentParser( @@ -26,73 +27,118 @@ def _handle_args(): help="Input catchment network", dest="supernetwork" ) - + parser.add_argument( + "-s", + "--subset", + help="Subset of catchments to process", + dest="subset", + nargs='+' + ) + parser.add_argument( + "-i", + "--input", + help="path to lateral flow inputs", + dest="input" + ) return parser.parse_args() -root = pathlib.Path('../..').resolve() +root = pathlib.Path('.').resolve() sys.path.append(r"../python_framework_v02") sys.path.append(r"../python_routing_v02") sys.path.append(r"../../test/input/next_gen") test_folder = pathlib.Path(root, "test") -next_gen_input_folder = test_folder.joinpath("input", "next_gen") -import nhd_network -import nhd_io +import troute.nhd_network as nhd_network +import troute.nhd_io as nhd_io import mc_reach def main(): args = _handle_args() + + next_gen_input_folder = test_folder.joinpath("input", "next_gen") + if args.input: + next_gen_input_folder = pathlib.Path(args.input) - #The following 2 values are currently hard coded for this test domain + # The following 2 values are currently hard coded for this test domain nts = 720 # number of timestep = 1140 * 60(model timestep) = 86400 = day dt_mc = 300.0 # time interval for MC - #Currently tested on the Sugar Creek domain - ngen_network_df = nhd_io.read_geopandas(os.path.join(next_gen_input_folder, args.supernetwork)) - - #Create dictionary mapping each connection ID - ngen_network_dict = dict(zip(ngen_network_df.ID, ngen_network_df.toID)) + # Currently tested on the Sugar Creek domain + ngen_network_df = nhd_io.read_geopandas( args.supernetwork ) + if args.subset: + ngen_network_df = ngen_network_df[ ngen_network_df['realized_catchment'].isin(args.subset) ] + + # Create dictionary mapping each connection ID + ngen_network_dict = dict(zip(ngen_network_df.id, ngen_network_df.toid)) + #ngen_network_dict = dict(zip(ngen_network_df.ID, ngen_network_df.toID)) def node_key_func(x): - return int(x[4:]) + return int(x[3:]) - #Extract the ID integer values + # Extract the ID integer values waterbody_connections = {node_key_func(k): node_key_func(v) for k, v in ngen_network_dict.items()} - #Convert dictionary connections to data frame and make ID column the index + # Convert dictionary connections to data frame and make ID column the index waterbody_df = pd.DataFrame.from_dict(waterbody_connections, orient='index', columns=['to']) - - #Sort ID index column + # Sort ID index column waterbody_df = waterbody_df.sort_index() waterbody_df = nhd_io.replace_downstreams(waterbody_df, "to", 0) connections = nhd_network.extract_connections(waterbody_df, "to") - #Read and convert catchment lateral flows to format that can be processed by compute_network + # Read and convert catchment lateral flows to format that can be processed by compute_network qlats = next_gen_io.read_catchment_lateral_flows(next_gen_input_folder) - + print(qlats) rconn = nhd_network.reverse_network(connections) subnets = nhd_network.reachable_network(rconn, check_disjoint=False) + + # read the routelink file + nhd_routelink = nhd_io.read_netcdf("data/RouteLink_NHDPLUS.nc") + nhd_routelink['dt'] = 300.0 + + nhd_routelink.set_index("link", inplace=True) - waterbody_df['dt'] = 300.0 + routelink_cols = { + "downstream": "to", + "dx": "Length", + "n": "n", + "ncc": "nCC", + "s0": "So", + "bw": "BtmWdth", + "tw": "TopWdth", + "twcc": "TopWdthCC", + "waterbody": "NHDWaterbodyComID", + "musk": "MusK", + "musx": "MusX", + "cs": "ChSlp", + } - #Setting all below to 1.0 until we can get the appropriate parameters - waterbody_df['bw'] = 1.0 - waterbody_df['tw'] = 1.0 - waterbody_df['twcc'] = 1.0 - waterbody_df['dx'] = 1.0 - waterbody_df['n'] = 1.0 - waterbody_df['ncc'] = 1.0 - waterbody_df['cs'] = 1.0 - waterbody_df['s0'] = 1.0 + routelink_cols = dict([(value,key) for key, value in routelink_cols.items() ]) + + nhd_routelink.rename(columns=routelink_cols, inplace=True) + + with open(next_gen_input_folder/'coarse/crosswalk.json') as f: + crosswalk_data = json.load(f) + waterbody_df['comid'] = waterbody_df.apply(lambda x: crosswalk_data['cat-' + str(x.name)]['outlet_COMID'], axis=1) + + waterbody_df = waterbody_df.join(nhd_routelink, on='comid', how='left') + + del nhd_routelink + + + # initial conditions, assume to be zero + # TO DO: Allow optional reading of initial conditions from WRF + q0 = pd.DataFrame( + 0, index=waterbody_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" + ) #Set types as float32 waterbody_df = waterbody_df.astype({"dt": "float32", "bw": "float32", "tw": "float32", "twcc": "float32", "dx": "float32", "n": "float32", "ncc": "float32", "cs": "float32", "s0": "float32"}) - + subreaches = {} for tw, net in subnets.items(): @@ -104,9 +150,12 @@ def node_key_func(x): for twi, (tw, reach) in enumerate(subreaches.items(), 1): r = list(chain.from_iterable(reach)) data_sub = waterbody_df.loc[r, ['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']].sort_index() + #data_sub = waterbody_df.loc[r, ['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] qlat_sub = qlats.loc[r].sort_index() + q0_sub = q0.loc[r].sort_index() + results.append(mc_reach.compute_network( - nts, reach, subnets[tw], data_sub.index.values, data_sub.columns.values, data_sub.values, qlat_sub.values + nts, reach, subnets[tw], data_sub.index.values, data_sub.columns.values, data_sub.values, qlat_sub.values, q0_sub.values ) ) diff --git a/test/input/next_gen/cat-87_output.csv b/test/input/next_gen/cat-58_output.csv similarity index 100% rename from test/input/next_gen/cat-87_output.csv rename to test/input/next_gen/cat-58_output.csv diff --git a/test/input/next_gen/cat-88_output.csv b/test/input/next_gen/cat-59_output.csv similarity index 100% rename from test/input/next_gen/cat-88_output.csv rename to test/input/next_gen/cat-59_output.csv diff --git a/test/input/next_gen/cat-89_output.csv b/test/input/next_gen/cat-60_output.csv similarity index 100% rename from test/input/next_gen/cat-89_output.csv rename to test/input/next_gen/cat-60_output.csv diff --git a/test/input/next_gen/cat-92_output.csv b/test/input/next_gen/cat-63_output.csv similarity index 100% rename from test/input/next_gen/cat-92_output.csv rename to test/input/next_gen/cat-63_output.csv diff --git a/test/input/next_gen/crosswalk.json b/test/input/next_gen/crosswalk.json new file mode 100644 index 000000000..0c8733226 --- /dev/null +++ b/test/input/next_gen/crosswalk.json @@ -0,0 +1,539 @@ +{ + "cat-1": { + "COMID": "9731278", + "outlet_COMID": 9731278 + }, + "cat-2": { + "COMID": "9731422", + "outlet_COMID": 9731422 + }, + "cat-3": { + "COMID": "9731394", + "outlet_COMID": 9731394 + }, + "cat-4": { + "COMID": "9731384", + "outlet_COMID": 9731384 + }, + "cat-5": { + "COMID": ["9731370", "9731362.2"], + "outlet_COMID": 9731370 + }, + "cat-6": { + "COMID": ["9731546", "9731388"], + "outlet_COMID": 9731388 + }, + "cat-7": { + "COMID": ["9731548", "9731402", "9731396", "9731526", "9731524"], + "outlet_COMID": 9731402 + }, + "cat-8": { + "COMID": ["9731408", "9731528"], + "outlet_COMID": 9731528 + }, + "cat-9": { + "COMID": "9731276", + "outlet_COMID": 9731276 + }, + "cat-10": { + "COMID": ["9731538", "9731330"], + "outlet_COMID": 9731330 + }, + "cat-11": { + "COMID": "9731354", + "outlet_COMID": 9731354 + }, + "cat-12": { + "COMID": "9731314", + "outlet_COMID": 9731314 + }, + "cat-13": { + "COMID": "9731374", + "outlet_COMID": 9731374 + }, + "cat-14": { + "COMID": ["9731544", "9731378", "9731522", "9731386"], + "outlet_COMID": 9731386 + }, + "cat-15": { + "COMID": "9731322", + "outlet_COMID": 9731322 + }, + "cat-16": { + "COMID": "9731372", + "outlet_COMID": 9731372 + }, + "cat-17": { + "COMID": "9731390", + "outlet_COMID": 9731390 + }, + "cat-18": { + "COMID": ["9731318", "9731502.1"], + "outlet_COMID": 9731502 + }, + "cat-19": { + "COMID": "9731474", + "outlet_COMID": 9731474 + }, + "cat-20": { + "COMID": "9731280", + "outlet_COMID": 9731280 + }, + "cat-21": { + "COMID": ["9731344", "9731340.2"], + "outlet_COMID": 9731344 + }, + "cat-22": { + "COMID": ["9731424", "9731434", "9731430"], + "outlet_COMID": 9731430 + }, + "cat-23": { + "COMID": "9731420", + "outlet_COMID": 9731420 + }, + "cat-24": { + "COMID": "9731368", + "outlet_COMID": 9731368 + }, + "cat-25": { + "COMID": "9731352", + "outlet_COMID": 9731352 + }, + "cat-26": { + "COMID": "9731332", + "outlet_COMID": 9731332 + }, + "cat-27": { + "COMID": "9731334", + "outlet_COMID": 9731334 + }, + "cat-28": { + "COMID": "9731290", + "outlet_COMID": 9731290 + }, + "cat-29": { + "COMID": ["9731270", "9731532", "9731514"], + "outlet_COMID": 9731514 + }, + "cat-30": { + "COMID": "9731400", + "outlet_COMID": 9731400 + }, + "cat-31": { + "COMID": "9731358", + "outlet_COMID": 9731358 + }, + "cat-32": { + "COMID": ["9731492", "9731288", "9731486.3"], + "outlet_COMID": 9731492 + }, + "cat-33": { + "COMID": ["9731360", "9731506"], + "outlet_COMID": 9731360 + }, + "cat-34": { + "COMID": "9731328", + "outlet_COMID": 9731328 + }, + "cat-35": { + "COMID": "9731294", + "outlet_COMID": 9731294 + }, + "cat-36": { + "COMID": "9731392", + "outlet_COMID": 9731392 + }, + "cat-37": { + "COMID": "9731380", + "outlet_COMID": 9731380 + }, + "cat-38": { + "COMID": ["9731540", "9731346"], + "outlet_COMID": 9731346 + }, + "cat-39": { + "COMID": ["9731542", "9731338", "9731518", "9731350", "9731520"], + "outlet_COMID": 9731338 + }, + "cat-40": { + "COMID": ["9731536", "9731326"], + "outlet_COMID": 9731326 + }, + "cat-41": { + "COMID": "9731306", + "outlet_COMID": 9731306 + }, + "cat-42": { + "COMID": "9731304", + "outlet_COMID": 9731304 + }, + "cat-43": { + "COMID": "9731282", + "outlet_COMID": 9731282 + }, + "cat-44": { + "COMID": ["9731534", "9731310"], + "outlet_COMID": 9731310 + }, + "cat-45": { + "COMID": "9731478", + "outlet_COMID": 9731478 + }, + "cat-46": { + "COMID": "9731490", + "outlet_COMID": 9731490 + }, + "cat-47": { + "COMID": "9731496", + "outlet_COMID": 9731496 + }, + "cat-48": { + "COMID": ["9731356", "9731366"], + "outlet_COMID": 9731356 + }, + "cat-49": { + "COMID": "9731382", + "outlet_COMID": 9731382 + }, + "cat-50": { + "COMID": "9731308", + "outlet_COMID": 9731308 + }, + "cat-51": { + "COMID": "9731298", + "outlet_COMID": 9731298 + }, + "cat-52": { + "COMID": ["9731516", "9731302"], + "outlet_COMID": 9731516 + }, + "cat-53": { + "COMID": "9731348", + "outlet_COMID": 9731348 + }, + "cat-54": { + "COMID": ["9731262", "9731258"], + "outlet_COMID": 9731262 + }, + "cat-55": { + "COMID": "9731432", + "outlet_COMID": 9731432 + }, + "cat-56": { + "COMID": "9731244", + "outlet_COMID": 9731244 + }, + "cat-57": { + "COMID": "9731274", + "outlet_COMID": 9731274 + }, + "cat-58": { + "COMID": "9731470", + "outlet_COMID": 9731470 + }, + "cat-59": { + "COMID": "9731256", + "outlet_COMID": 9731256 + }, + "cat-60": { + "COMID": "9731252", + "outlet_COMID": 9731252 + }, + "cat-61": { + "COMID": "9731240", + "outlet_COMID": 9731240 + }, + "cat-62": { + "COMID": "9731236", + "outlet_COMID": 9731236 + }, + "cat-63": { + "COMID": ["9731530", "9731238"], + "outlet_COMID": 9731238 + }, + "cat-64": { + "COMID": "9731246", + "outlet_COMID": 9731246 + }, + "cat-65": { + "COMID": "9731472", + "outlet_COMID": 9731472 + }, + "cat-66": { + "COMID": ["9731242", "9731250", "9731512"], + "outlet_COMID": 9731250 + }, + "cat-67": { + "COMID": "9731254", + "outlet_COMID": 9731254 + }, + "cat-68": { + "COMID": "9731320.1", + "outlet_COMID": 9731320 + }, + "cat-69": { + "COMID": "9731320.2", + "site_no": "0214678175", + "outlet_COMID": 9731320 + }, + "cat-70": { + "COMID": "9731364.1", + "outlet_COMID": 9731364 + }, + "cat-71": { + "COMID": "9731364.2", + "outlet_COMID": 9731364 + }, + "cat-72": { + "COMID": "9731362.1", + "outlet_COMID": 9731362 + }, + "cat-73": { + "COMID": "9731312.1", + "outlet_COMID": 9731312 + }, + "cat-74": { + "COMID": "9731312.2", + "outlet_COMID": 9731312 + }, + "cat-75": { + "COMID": "9731428.1", + "outlet_COMID": 9731428 + }, + "cat-76": { + "COMID": "9731428.2", + "outlet_COMID": 9731428 + }, + "cat-77": { + "COMID": "9731398.1", + "outlet_COMID": 9731398 + }, + "cat-78": { + "COMID": "9731398.2", + "outlet_COMID": 9731398 + }, + "cat-79": { + "COMID": "9731502.2", + "outlet_COMID": 9731502 + }, + "cat-80": { + "COMID": "9731502.3", + "outlet_COMID": 9731502 + }, + "cat-81": { + "COMID": "9731324.1", + "outlet_COMID": 9731324 + }, + "cat-82": { + "COMID": "9731324.2", + "outlet_COMID": 9731324 + }, + "cat-83": { + "COMID": "9731340.1", + "outlet_COMID": 9731340 + }, + "cat-84": { + "COMID": "9731316.1", + "outlet_COMID": 9731316 + }, + "cat-85": { + "COMID": "9731316.2", + "outlet_COMID": 9731316 + }, + "cat-86": { + "COMID": "9731498.1", + "outlet_COMID": 9731498 + }, + "cat-87": { + "COMID": "9731498.2", + "outlet_COMID": 9731498 + }, + "cat-88": { + "COMID": "9731504.1", + "outlet_COMID": 9731504 + }, + "cat-89": { + "COMID": "9731504.2", + "outlet_COMID": 9731504 + }, + "cat-90": { + "COMID": "9731486.1", + "outlet_COMID": 9731486 + }, + "cat-91": { + "COMID": "9731486.2", + "outlet_COMID": 9731486 + }, + "cat-92": { + "COMID": "9731272.1", + "outlet_COMID": 9731272 + }, + "cat-93": { + "COMID": "9731272.2", + "outlet_COMID": 9731272 + }, + "cat-94": { + "COMID": "9731482.1", + "outlet_COMID": 9731482 + }, + "cat-95": { + "COMID": "9731482.2", + "site_no": "02146300", + "outlet_COMID": 9731482 + }, + "cat-96": { + "COMID": "9731248.1", + "outlet_COMID": 9731248 + }, + "cat-97": { + "COMID": "9731248.2", + "site_no": "02146211", + "outlet_COMID": 9731248 + }, + "cat-98": { + "COMID": "9731468.1", + "outlet_COMID": 9731468 + }, + "cat-99": { + "COMID": "9731468.2", + "outlet_COMID": 9731468 + }, + "cat-100": { + "COMID": "9731300.1", + "outlet_COMID": 9731300 + }, + "cat-101": { + "COMID": "9731300.2", + "site_no": "02146348", + "outlet_COMID": 9731300 + }, + "cat-102": { + "COMID": "9731286.1", + "outlet_COMID": 9731286 + }, + "cat-103": { + "COMID": "9731286.2", + "outlet_COMID": 9731286 + }, + "cat-104": { + "COMID": "9731286.3", + "site_no": "02146562", + "outlet_COMID": 9731286 + }, + "cat-105": { + "COMID": "9731342.1", + "outlet_COMID": 9731342 + }, + "cat-106": { + "COMID": "9731342.2", + "site_no": "02146670", + "outlet_COMID": 9731342 + }, + "cat-107": { + "COMID": "9731500.1", + "outlet_COMID": 9731500 + }, + "cat-108": { + "COMID": "9731500.2", + "site_no": "02146600", + "outlet_COMID": 9731500 + }, + "cat-109": { + "COMID": "9731284.1", + "outlet_COMID": 9731284 + }, + "cat-110": { + "COMID": "9731284.2", + "site_no": "0214655255", + "outlet_COMID": 9731284 + }, + "cat-111": { + "COMID": "9731296.1", + "outlet_COMID": 9731296 + }, + "cat-112": { + "COMID": "9731296.2", + "site_no": "02146507", + "outlet_COMID": 9731296 + }, + "cat-113": { + "COMID": "9731484.1", + "outlet_COMID": 9731484 + }, + "cat-114": { + "COMID": "9731484.2", + "outlet_COMID": 9731484 + }, + "cat-115": { + "COMID": "9731484.3", + "site_no": "0214645022", + "outlet_COMID": 9731484 + }, + "cat-116": { + "COMID": "9731260.1", + "outlet_COMID": 9731260 + }, + "cat-117": { + "COMID": "9731260.2", + "site_no": "0214642825", + "outlet_COMID": 9731260 + }, + "cat-118": { + "COMID": "9731264.1", + "outlet_COMID": 9731264 + }, + "cat-119": { + "COMID": "9731264.2", + "site_no": "02146285", + "outlet_COMID": 9731264 + }, + "cat-120": { + "COMID": "9731476.1", + "outlet_COMID": 9731476 + }, + "cat-121": { + "COMID": "9731476.2", + "site_no": "0214627970", + "outlet_COMID": 9731476 + }, + "cat-122": { + "COMID": "9731292.1", + "outlet_COMID": 9731292 + }, + "cat-123": { + "COMID": "9731292.2", + "outlet_COMID": 9731292 + }, + "cat-124": { + "COMID": "9731292.3", + "site_no": "0214657975", + "outlet_COMID": 9731292 + }, + "cat-125": { + "COMID": "9731494.1", + "outlet_COMID": 9731494 + }, + "cat-126": { + "COMID": "9731494.2", + "site_no": "02146700", + "outlet_COMID": 9731494 + }, + "cat-127": { + "COMID": "9731488.1", + "outlet_COMID": 9731488 + }, + "cat-128": { + "COMID": "9731488.2", + "site_no": "02146470", + "outlet_COMID": 9731488 + }, + "cat-129": { + "COMID": "9731454.1", + "outlet_COMID": 9731454 + }, + "cat-130": { + "COMID": "9731454.2", + "site_no": "02146800", + "outlet_COMID": 9731454 + } +} diff --git a/test/input/next_gen/flowpath_data.geojson b/test/input/next_gen/flowpath_data.geojson new file mode 100644 index 000000000..cbb99b4ae --- /dev/null +++ b/test/input/next_gen/flowpath_data.geojson @@ -0,0 +1,137 @@ +{ +"type": "FeatureCollection", +"name": "flowpath_data", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, +"features": [ +{ "type": "Feature", "id": "fp-1", "properties": { "length_km": 3.926, "slope_percent": 0.00517871, "main_id": 81, "toid": "fp-100", "realized_catchment": "cat-1" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.943193717999975, 35.211400078110877 ], [ -80.94509018499997, 35.210070611110787 ], [ -80.946177784999975, 35.209199611110876 ], [ -80.946400784999966, 35.208901811110913 ], [ -80.94681858499996, 35.207870811110929 ], [ -80.947041517999921, 35.207595878110865 ], [ -80.947850184999936, 35.206954078110876 ], [ -80.948129317999985, 35.206839411110948 ], [ -80.948379784999986, 35.206243678110923 ], [ -80.94826758499994, 35.205602478110904 ], [ -80.947764584999959, 35.204640878110958 ], [ -80.947122184999969, 35.203977078110817 ], [ -80.94701011799998, 35.203381611110935 ], [ -80.947845517999951, 35.201480078111011 ], [ -80.947929184999978, 35.201434211111028 ], [ -80.94798478499996, 35.201182278110984 ], [ -80.948179984999967, 35.200998878110994 ], [ -80.948318984999958, 35.200472078110991 ], [ -80.948344917999975, 35.198204611111045 ], [ -80.948093317999962, 35.197723811110926 ], [ -80.947758184999927, 35.197334611111039 ], [ -80.946780917999945, 35.196648078111018 ], [ -80.945524784999975, 35.196007611111035 ], [ -80.944157384999983, 35.195802278111124 ], [ -80.943906317999961, 35.195619211111023 ], [ -80.943710717999977, 35.19525281111104 ], [ -80.943710317999944, 35.19477181111106 ], [ -80.943988784999988, 35.194199078111048 ], [ -80.944016317999967, 35.193764011111014 ], [ -80.943541317999973, 35.19291681111109 ], [ -80.943485317999944, 35.192642011111104 ], [ -80.943708317999977, 35.192412811110962 ], [ -80.944572917999949, 35.192022878111004 ], [ -80.944628584999975, 35.191908411111037 ], [ -80.94443318499998, 35.191702411111031 ], [ -80.944042384999989, 35.19154221111107 ], [ -80.942563584999959, 35.191451478111084 ], [ -80.942312317999964, 35.19124547811105 ], [ -80.942060917999981, 35.190856278111106 ], [ -80.941948784999965, 35.190123411111117 ], [ -80.941864984999953, 35.19007761111105 ], [ -80.941864718, 35.189619611111141 ], [ -80.942226784999932, 35.188978078111106 ], [ -80.943174917999954, 35.18842781111109 ], [ -80.943174584999952, 35.188061411111015 ], [ -80.942923184999984, 35.18767221111105 ], [ -80.942726984999979, 35.186595811111175 ], [ -80.94214098499998, 35.186390011111122 ], [ -80.941554384999961, 35.185451278111159 ], [ -80.941107785000014, 35.185359878111086 ], [ -80.940019517999986, 35.184856611111144 ], [ -80.939489517999959, 35.184834011111143 ] ] } }, +{ "type": "Feature", "id": "fp-2", "properties": { "length_km": 4.778, "slope_percent": 0.00154248, "main_id": 23, "toid": "fp-22", "realized_catchment": "cat-2" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.934390117999953, 35.036648478112802 ], [ -80.933833117999939, 35.036625811112742 ], [ -80.933248384999956, 35.036740611112812 ], [ -80.932830717999934, 35.036740878112745 ], [ -80.931911517999936, 35.036443478112844 ], [ -80.930268517999934, 35.036398478112758 ], [ -80.929599917999951, 35.03605521111281 ], [ -80.92959938499996, 35.035345211112777 ], [ -80.930044917999965, 35.035138811112837 ], [ -80.930044517999988, 35.034657811112808 ], [ -80.929960917999949, 35.034589078112909 ], [ -80.929932717999975, 35.033925011112807 ], [ -80.930155117999959, 35.033420878112828 ], [ -80.929737317999979, 35.033306611112835 ], [ -80.928456717999936, 35.033696611112781 ], [ -80.927843985, 35.033719811112839 ], [ -80.927370384999975, 35.033284811112821 ], [ -80.927203117999966, 35.032918411112853 ], [ -80.927202184999956, 35.031589878112925 ], [ -80.927062184999954, 35.030444811112872 ], [ -80.926978718, 35.03039907811285 ], [ -80.926615717999937, 35.028841678112869 ], [ -80.926225717999955, 35.028521278112883 ], [ -80.925278784999989, 35.028223878112897 ], [ -80.924916584999949, 35.027811811112883 ], [ -80.924915584999951, 35.026345878112899 ], [ -80.92480411799994, 35.026071078112942 ], [ -80.924330717999965, 35.025933878112923 ], [ -80.923718317999928, 35.026163211112888 ], [ -80.92324498499994, 35.026232078112919 ], [ -80.922437317999936, 35.026026211112871 ], [ -80.922214517999976, 35.025866011112861 ], [ -80.922130184999958, 35.024514678112908 ], [ -80.921935117999965, 35.024217011112952 ], [ -80.921573117999969, 35.024102611112916 ], [ -80.921099784999967, 35.024331811112965 ], [ -80.921072384999945, 35.02495027811301 ], [ -80.92096111799998, 35.025156478112947 ], [ -80.920237184999962, 35.025339878112923 ], [ -80.919958984999937, 35.025569078112945 ], [ -80.919959317999954, 35.026210411112906 ], [ -80.919848117999962, 35.026485278112894 ], [ -80.919486184999982, 35.02678321111302 ], [ -80.919040784999936, 35.026897878112869 ], [ -80.918483984999966, 35.026898078112893 ], [ -80.918094117999942, 35.026646411112878 ], [ -80.917648184999962, 35.026188478112886 ], [ -80.917647917999943, 35.025547078112879 ], [ -80.917870517999972, 35.025340878112907 ], [ -80.918232517999968, 35.025249211112964 ], [ -80.918844784999976, 35.024722011112964 ], [ -80.918761117999949, 35.024355611113016 ], [ -80.917953317999945, 35.023691678112968 ], [ -80.917841584999962, 35.023233811112945 ], [ -80.916727717999947, 35.022798878113008 ], [ -80.916281984999955, 35.022524211112973 ], [ -80.916281384999934, 35.021172878113028 ], [ -80.916587517999972, 35.020897878112976 ], [ -80.916587384999957, 35.020691811112982 ], [ -80.916197317999945, 35.020027678113053 ], [ -80.916196984999956, 35.019409278112946 ], [ -80.915973784999949, 35.018401611113035 ], [ -80.915667384999949, 35.017989411113021 ], [ -80.915305384999954, 35.01787501111297 ], [ -80.914637117999973, 35.017921011113067 ], [ -80.914052384999934, 35.017852478112992 ], [ -80.912966584999936, 35.017898611113068 ], [ -80.912159517999953, 35.018471478112929 ], [ -80.911797584999988, 35.018631878112991 ], [ -80.911185117999935, 35.018700811113007 ], [ -80.910683984999935, 35.018586478112965 ], [ -80.909458584999982, 35.017922678112981 ] ] } }, +{ "type": "Feature", "id": "fp-3", "properties": { "length_km": 1.868, "slope_percent": 0.00123661, "main_id": 23, "toid": "fp-2", "realized_catchment": "cat-3" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.942807984999945, 35.046447011112633 ], [ -80.942723985, 35.045897278112662 ], [ -80.942528784999979, 35.045576678112724 ], [ -80.941915717999962, 35.045096078112721 ], [ -80.941692784999987, 35.045027478112694 ], [ -80.940940917999967, 35.045005011112721 ], [ -80.939325784999937, 35.045303678112717 ], [ -80.938713117999953, 35.045304011112755 ], [ -80.938573717999986, 35.045143678112687 ], [ -80.938573317999939, 35.04477721111266 ], [ -80.939046717999986, 35.044593811112712 ], [ -80.939074384999969, 35.04438761111264 ], [ -80.938711584999965, 35.043425811112748 ], [ -80.938432918000018, 35.04315107811275 ], [ -80.937903717999973, 35.043059811112691 ], [ -80.937680717999967, 35.042785011112699 ], [ -80.937680317999934, 35.042258211112731 ], [ -80.937819317999981, 35.041777211112709 ], [ -80.937986117999969, 35.041525211112734 ], [ -80.938431717999947, 35.041410411112743 ], [ -80.93893271799999, 35.041066611112726 ], [ -80.939377117999967, 35.03953181111271 ], [ -80.939404384999989, 35.038936278112672 ], [ -80.939153517999955, 35.038569878112767 ], [ -80.935727117999988, 35.037128678112772 ], [ -80.934529384999962, 35.036785811112757 ], [ -80.934390117999953, 35.036648478112802 ] ] } }, +{ "type": "Feature", "id": "fp-4", "properties": { "length_km": 1.644, "slope_percent": 0.00226885, "main_id": 23, "toid": "fp-3", "realized_catchment": "cat-4" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.945715117999953, 35.058928011112528 ], [ -80.945213384999988, 35.058516011112545 ], [ -80.94490671799997, 35.058149678112613 ], [ -80.94418191799997, 35.057508811112548 ], [ -80.943234384999982, 35.056982478112552 ], [ -80.943206384999954, 35.056753478112576 ], [ -80.943401317999985, 35.056455678112641 ], [ -80.943651184999965, 35.055722611112593 ], [ -80.943650917999946, 35.055287411112552 ], [ -80.943399917999955, 35.054829411112593 ], [ -80.942145517999947, 35.053639278112598 ], [ -80.941448317999956, 35.052586078112512 ], [ -80.941057718, 35.051715878112581 ], [ -80.940890317999958, 35.051532678112629 ], [ -80.940471917999957, 35.050662611112585 ], [ -80.940387717999968, 35.049883878112659 ], [ -80.941138584999976, 35.048486278112598 ], [ -80.942446184999937, 35.046722011112671 ], [ -80.942807984999945, 35.046447011112633 ] ] } }, +{ "type": "Feature", "id": "fp-5", "properties": { "length_km": 3.4061088560642796, "slope_percent": 0.000541165, "main_id": 23, "toid": "fp-4", "realized_catchment": "cat-5" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.94910704541212, 35.083250370852269 ], [ -80.948437784999967, 35.082448878112245 ], [ -80.948409717999937, 35.082059478112292 ], [ -80.948520984999959, 35.081899078112272 ], [ -80.948938384999963, 35.081440678112259 ], [ -80.949077317999979, 35.080868011112308 ], [ -80.948826184999973, 35.080295478112255 ], [ -80.948853317999976, 35.079516811112299 ], [ -80.949187517999974, 35.079310478112291 ], [ -80.950803184999984, 35.079034611112355 ], [ -80.951053518, 35.078553478112354 ], [ -80.951052718, 35.077728878112339 ], [ -80.950662317999956, 35.077385611112355 ], [ -80.950132784999951, 35.077134011112399 ], [ -80.94887878499999, 35.076882878112322 ], [ -80.948655717999941, 35.076631078112307 ], [ -80.94862758499994, 35.076447811112367 ], [ -80.948544117999958, 35.076402078112338 ], [ -80.948264784999935, 35.075715078112388 ], [ -80.948264517999974, 35.075371611112331 ], [ -80.948570717999971, 35.075096611112365 ], [ -80.949350784999979, 35.07507307811241 ], [ -80.94951791799997, 35.075004278112395 ], [ -80.949712584999972, 35.074614811112369 ], [ -80.949655917999962, 35.07358421111234 ], [ -80.949989117999962, 35.072324211112409 ], [ -80.949905384999965, 35.072278478112381 ], [ -80.949905317999935, 35.07211821111234 ], [ -80.949737917999983, 35.071820478112393 ], [ -80.949681784999925, 35.071454011112372 ], [ -80.94976518499999, 35.07127081111237 ], [ -80.948761584999943, 35.070469878112384 ], [ -80.948677384999954, 35.069714078112447 ], [ -80.948537784999985, 35.069462211112395 ], [ -80.948426117999986, 35.069095678112426 ], [ -80.947979717999942, 35.068294478112414 ], [ -80.947171117999972, 35.067470411112403 ], [ -80.946668984999974, 35.066806411112488 ], [ -80.946557184999961, 35.066302611112455 ], [ -80.946835117999967, 35.06550081111245 ], [ -80.947112717999971, 35.064401211112482 ], [ -80.948170717999972, 35.063736411112458 ], [ -80.948198317999982, 35.063553078112463 ], [ -80.94855998499996, 35.062934478112446 ], [ -80.94867078499999, 35.062224478112512 ], [ -80.948670117999939, 35.061514411112519 ], [ -80.948028917999977, 35.060919278112571 ], [ -80.946830384999927, 35.060164078112507 ], [ -80.945882517999962, 35.059225611112481 ], [ -80.945715117999953, 35.058928011112528 ] ] } }, +{ "type": "Feature", "id": "fp-6", "properties": { "length_km": 0.551, "slope_percent": 0.01320146, "main_id": 72, "toid": "fp-17", "realized_catchment": "cat-6" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.983358317999944, 35.045752878112637 ], [ -80.979624984999987, 35.044771278112677 ], [ -80.977704317999951, 35.045574678112722 ] ] } }, +{ "type": "Feature", "id": "fp-7", "properties": { "length_km": 2.801, "slope_percent": 0.006955728, "main_id": 38, "toid": "fp-2", "realized_catchment": "cat-7" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.95877978499999, 35.031892611112916 ], [ -80.953073784999958, 35.034393078112821 ], [ -80.952461784999969, 35.035034811112808 ], [ -80.952406517999975, 35.035607411112792 ], [ -80.95190571799999, 35.035905478112809 ], [ -80.951710917999947, 35.036180478112797 ], [ -80.951376917999951, 35.036295211112801 ], [ -80.950485784999955, 35.036295811112808 ], [ -80.950151584999958, 35.036456278112787 ], [ -80.949539517999938, 35.037120878112667 ], [ -80.949316784999951, 35.037098211112784 ], [ -80.948536317999981, 35.036205278112739 ], [ -80.947393784999974, 35.035335678112823 ], [ -80.946975917999964, 35.035152678112844 ], [ -80.946800184999972, 35.03513181111277 ], [ -80.945108317999953, 35.034862878112797 ], [ -80.944145384999956, 35.033788211112807 ], [ -80.942259384999943, 35.033137278112854 ], [ -80.940498184999967, 35.03440307811276 ], [ -80.938772184999948, 35.03400841111285 ], [ -80.938537317999987, 35.033989411112749 ], [ -80.938091785, 35.034012611112793 ], [ -80.937618584999953, 35.03426467811282 ], [ -80.936672384999952, 35.03502107811277 ], [ -80.935725984999962, 35.035594211112787 ], [ -80.935559184999988, 35.036006611112768 ], [ -80.934390117999953, 35.036648478112802 ] ] } }, +{ "type": "Feature", "id": "fp-8", "properties": { "length_km": 1.094, "slope_percent": 0.00238595, "main_id": 65, "toid": "fp-2", "realized_catchment": "cat-8" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.941932584999947, 35.031399411112851 ], [ -80.942156117999957, 35.032246678112763 ], [ -80.942241784999965, 35.032397411112854 ], [ -80.942363984999986, 35.032607678112875 ], [ -80.942259384999943, 35.033137278112854 ] ] } }, +{ "type": "Feature", "id": "fp-9", "properties": { "length_km": 0.206, "slope_percent": 1.0000000000000001e-005, "main_id": 119, "toid": "fp-100", "realized_catchment": "cat-9" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.938318917999936, 35.186277611111144 ], [ -80.939266984999961, 35.185658678111118 ], [ -80.939461917999964, 35.185269078111126 ], [ -80.939489517999959, 35.184834011111143 ] ] } }, +{ "type": "Feature", "id": "fp-10", "properties": { "length_km": 2.84, "slope_percent": 1.0000000000000001e-005, "main_id": 76, "toid": "fp-33", "realized_catchment": "cat-10" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.922340917999975, 35.101632678112033 ], [ -80.920164584999981, 35.096961078112109 ], [ -80.919885984999937, 35.096915411112107 ], [ -80.917795184999989, 35.095633478112092 ], [ -80.916624517999935, 35.095382011112115 ], [ -80.916011517999948, 35.095359278112149 ], [ -80.914896917999968, 35.095497211112125 ], [ -80.913224984999943, 35.095932878112116 ], [ -80.911775517999956, 35.09515461111215 ], [ -80.910799784999938, 35.094307411112112 ], [ -80.909935784999959, 35.093964078112151 ], [ -80.907928584999979, 35.092224011112194 ], [ -80.906952984999975, 35.091262411112162 ], [ -80.906785517999936, 35.090689811112242 ], [ -80.906701917999953, 35.090644011112225 ], [ -80.906673984999941, 35.090460811112258 ], [ -80.906116517999976, 35.090209011112201 ], [ -80.906032784999979, 35.089728078112195 ], [ -80.903775317999987, 35.088835278112221 ], [ -80.903050717999974, 35.088400278112211 ], [ -80.902186717999939, 35.087644678112198 ], [ -80.90107158499994, 35.086293611112268 ] ] } }, +{ "type": "Feature", "id": "fp-11", "properties": { "length_km": 3.571, "slope_percent": 0.00653004, "main_id": 105, "toid": "fp-30", "realized_catchment": "cat-11" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.928231384999961, 35.073527011112382 ], [ -80.926698784999985, 35.073023678112428 ], [ -80.925584184999934, 35.072543211112361 ], [ -80.925054717999956, 35.072520478112381 ], [ -80.923578384999971, 35.072796078112461 ], [ -80.922296784999958, 35.072796611112381 ], [ -80.921850984999935, 35.072544811112444 ], [ -80.921070717999953, 35.072201611112419 ], [ -80.920345984999983, 35.071675011112468 ], [ -80.920122917999947, 35.071285678112339 ], [ -80.919537584999944, 35.070873678112434 ], [ -80.918980317999967, 35.070644811112437 ], [ -80.918367384999954, 35.07066787811241 ], [ -80.917531917999938, 35.071103411112475 ], [ -80.916445517999989, 35.071287011112389 ], [ -80.916306117999966, 35.0711268111124 ], [ -80.916222317999939, 35.070691678112397 ], [ -80.915469584999983, 35.069638278112393 ], [ -80.91544171799994, 35.069501011112422 ], [ -80.914661184999943, 35.068447611112475 ], [ -80.914577384999973, 35.068172811112483 ], [ -80.914075584999978, 35.067371278112411 ], [ -80.912710317999938, 35.066822078112551 ], [ -80.912125384999968, 35.06705127811248 ], [ -80.911456984999973, 35.067624011112542 ], [ -80.911373717999922, 35.068105078112495 ], [ -80.910482517999966, 35.068769478112394 ], [ -80.910426984999958, 35.069296411112397 ], [ -80.910594317999937, 35.069662811112373 ], [ -80.910510784999985, 35.069960611112435 ], [ -80.910343784999952, 35.070166678112443 ], [ -80.910037517999967, 35.070327078112463 ], [ -80.909117984999952, 35.070212878112464 ], [ -80.908811517999936, 35.070029678112384 ], [ -80.908476984999936, 35.069319811112514 ], [ -80.907780384999967, 35.069136878112431 ], [ -80.907223184999964, 35.069205811112411 ] ] } }, +{ "type": "Feature", "id": "fp-12", "properties": { "length_km": 1.471, "slope_percent": 0.00575, "main_id": 78, "toid": "fp-15", "realized_catchment": "cat-12" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.999798117999944, 35.116075078111862 ], [ -80.999087184999965, 35.11559501111185 ], [ -80.998584184999984, 35.114816811111965 ], [ -80.998277317999964, 35.114519278111914 ], [ -80.997440184999959, 35.113993411111849 ], [ -80.995822517999954, 35.113262011111885 ], [ -80.995208384999955, 35.112667211111969 ], [ -80.99272478499995, 35.110699878111916 ], [ -80.991469584999948, 35.110014078111959 ], [ -80.990772517999986, 35.109808611111887 ], [ -80.987845584999945, 35.109628078111896 ], [ -80.987455584999964, 35.109720011111968 ], [ -80.986842384999989, 35.109743478112001 ], [ -80.986758584999961, 35.10958321111206 ] ] } }, +{ "type": "Feature", "id": "fp-13", "properties": { "length_km": 2.129, "slope_percent": 0.01376767, "main_id": 74, "toid": "fp-16", "realized_catchment": "cat-13" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.975663384999962, 35.062021611112456 ], [ -80.975412717999987, 35.062021678112551 ], [ -80.974465184999971, 35.061793478112577 ], [ -80.972737317999986, 35.061084878112538 ], [ -80.971008584999936, 35.059666211112493 ], [ -80.970060784999987, 35.059071411112534 ], [ -80.96928058499999, 35.058797211112527 ], [ -80.96808211799997, 35.058202611112549 ], [ -80.966103717999943, 35.057539878112543 ], [ -80.965490317999979, 35.057059411112554 ], [ -80.963595984999984, 35.056900478112532 ], [ -80.963150184999961, 35.056740478112552 ], [ -80.962787717999959, 35.056351411112566 ], [ -80.962425317999987, 35.056099678112588 ], [ -80.961283117999983, 35.05589441111259 ], [ -80.96083731799996, 35.055711478112528 ], [ -80.960391117999961, 35.055299611112588 ], [ -80.959973184999953, 35.055185278112631 ], [ -80.95930471799997, 35.055162878112654 ], [ -80.957438717999935, 35.05534741111267 ], [ -80.956686784999988, 35.055554078112607 ], [ -80.955991384999948, 35.056447811112555 ], [ -80.955490117999943, 35.056608478112501 ] ] } }, +{ "type": "Feature", "id": "fp-14", "properties": { "length_km": 0.969, "slope_percent": 0.00745173, "main_id": 39, "toid": "fp-17", "realized_catchment": "cat-14" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.979107384999978, 35.054002078112532 ], [ -80.978239984999959, 35.050819211112689 ], [ -80.978350784999975, 35.05029227811265 ], [ -80.978325117999987, 35.050135411112585 ], [ -80.978541317999941, 35.049222478112611 ], [ -80.977982784999938, 35.047667611112637 ], [ -80.977929384999982, 35.047521278112676 ], [ -80.977900584999986, 35.046719678112645 ], [ -80.977983384999959, 35.046009611112659 ], [ -80.977704317999951, 35.045574678112722 ] ] } }, +{ "type": "Feature", "id": "fp-15", "properties": { "length_km": 3.571, "slope_percent": 0.00296835, "main_id": 78, "toid": "fp-72", "realized_catchment": "cat-15" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.986758584999961, 35.10958321111206 ], [ -80.986674384999944, 35.109171078111956 ], [ -80.986338917999944, 35.108415611112008 ], [ -80.98538918499996, 35.106950678112035 ], [ -80.983686918, 35.105257278112028 ], [ -80.982347784999945, 35.104227811112018 ], [ -80.98212431799999, 35.10386147811203 ], [ -80.981900384999975, 35.103128878112052 ], [ -80.981173784999967, 35.101503278112077 ], [ -80.980336384999987, 35.100519078112043 ], [ -80.980084984999962, 35.100084211112083 ], [ -80.978495917999965, 35.099696211112089 ], [ -80.97729678499995, 35.099124678112105 ], [ -80.976599517999929, 35.098598478112017 ], [ -80.976153317999945, 35.098369811112057 ], [ -80.973309784999969, 35.097593411112094 ], [ -80.970884384999962, 35.096862478112101 ], [ -80.966311784999959, 35.09487341111209 ], [ -80.964750384999945, 35.094050011112159 ], [ -80.964081385, 35.093913011112114 ], [ -80.963551984999981, 35.093913478112128 ], [ -80.962437784999963, 35.094395278112145 ], [ -80.960933917999967, 35.09531247811217 ], [ -80.960181717999944, 35.095610811112195 ], [ -80.959429384999964, 35.095680078112082 ], [ -80.958760384999948, 35.095497278112155 ], [ -80.95742171799999, 35.094421678112148 ] ] } }, +{ "type": "Feature", "id": "fp-16", "properties": { "length_km": 1.106, "slope_percent": 0.00981916, "main_id": 74, "toid": "fp-4", "realized_catchment": "cat-16" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.955490117999943, 35.056608478112501 ], [ -80.95471011799998, 35.056494411112602 ], [ -80.954041717999971, 35.056609411112596 ], [ -80.953707717999976, 35.056884478112565 ], [ -80.953373984999985, 35.057411478112563 ], [ -80.952900717999952, 35.057595011112561 ], [ -80.952566584999943, 35.057847211112545 ], [ -80.952038184999935, 35.058740811112528 ], [ -80.95173191799995, 35.058878478112497 ], [ -80.949921117999963, 35.05876507811255 ], [ -80.949698117999958, 35.058650811112592 ], [ -80.949474984999938, 35.058147011112553 ], [ -80.949112584999966, 35.057941011112518 ], [ -80.948081984999988, 35.057941678112577 ], [ -80.947692117999964, 35.05805647811264 ], [ -80.947302517999972, 35.058469011112543 ], [ -80.946829118, 35.058721078112519 ], [ -80.945715117999953, 35.058928011112528 ] ] } }, +{ "type": "Feature", "id": "fp-17", "properties": { "length_km": 3.529, "slope_percent": 0.00443751, "main_id": 39, "toid": "fp-3", "realized_catchment": "cat-17" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.977704317999951, 35.045574678112722 ], [ -80.976478184999962, 35.044957278112705 ], [ -80.975920984999959, 35.044797411112633 ], [ -80.974723184999959, 35.044615211112756 ], [ -80.973970717999975, 35.044203478112699 ], [ -80.972745317999966, 35.044158678112701 ], [ -80.972299385, 35.043975811112716 ], [ -80.971909517999961, 35.04399901111276 ], [ -80.971129984999948, 35.044274478112719 ], [ -80.968625184999951, 35.045788211112779 ], [ -80.967622917999961, 35.046178411112699 ], [ -80.966202984999939, 35.046591678112648 ], [ -80.965200917999937, 35.047027678112677 ], [ -80.962555717999976, 35.047648011112607 ], [ -80.961079117999986, 35.047191011112673 ], [ -80.960104317999935, 35.047146011112659 ], [ -80.958015785, 35.047491011112669 ], [ -80.957236184999942, 35.047766278112661 ], [ -80.956595984999979, 35.048133211112614 ], [ -80.956261784999981, 35.048110611112648 ], [ -80.955648717999964, 35.047744478112627 ], [ -80.955230784999969, 35.047676011112657 ], [ -80.954673984999943, 35.04785967811268 ], [ -80.954089584999977, 35.048318078112693 ], [ -80.953866784999946, 35.048409878112665 ], [ -80.95255771799998, 35.048387811112654 ], [ -80.951583384999935, 35.048869478112721 ], [ -80.950831384999958, 35.048984478112658 ], [ -80.949048784999945, 35.048825278112673 ], [ -80.945567317999974, 35.048873211112642 ], [ -80.944926584999962, 35.048690278112645 ], [ -80.94448071799998, 35.048415678112697 ], [ -80.944424784999967, 35.048209611112632 ], [ -80.943449317999978, 35.047225411112649 ], [ -80.943058917999963, 35.046652878112717 ], [ -80.942807984999945, 35.046447011112633 ] ] } }, +{ "type": "Feature", "id": "fp-18", "properties": { "length_km": 4.4420571469417309, "slope_percent": 0.014661715, "main_id": 24, "toid": "fp-79", "realized_catchment": "cat-18" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.695108918999949, 35.106937678111954 ], [ -80.696224118999965, 35.1073722111121 ], [ -80.696670318999963, 35.107738478111976 ], [ -80.697729584999934, 35.107966811111943 ], [ -80.698036584999954, 35.108333078111997 ], [ -80.698064518999956, 35.108516278112035 ], [ -80.698343584999975, 35.10888261111193 ], [ -80.699040584999977, 35.10913421111195 ], [ -80.699207984999987, 35.109294411111968 ], [ -80.699570518999963, 35.109454478111957 ], [ -80.700768984999982, 35.109591078111897 ], [ -80.701633518999984, 35.110117411112 ], [ -80.702163584999923, 35.110598078111948 ], [ -80.70263751899995, 35.110758078111964 ], [ -80.703306384999962, 35.110757678111995 ], [ -80.703891384999977, 35.110390878112035 ], [ -80.70478258499999, 35.109542811111915 ], [ -80.706036118999975, 35.10887767811198 ], [ -80.707985984999937, 35.107616678111995 ], [ -80.710214584999946, 35.106767678112021 ], [ -80.71040938499992, 35.106424078112077 ], [ -80.711718917999974, 35.105965078112 ], [ -80.712024984999971, 35.105621211112044 ], [ -80.712191917999974, 35.105277611112122 ], [ -80.712972184999956, 35.105231211111978 ], [ -80.71378018499999, 35.104887078112021 ], [ -80.714448584999957, 35.104451478111997 ], [ -80.714671117999956, 35.104107678112079 ], [ -80.714670517999949, 35.103489278112008 ], [ -80.714809317999936, 35.103054011112093 ], [ -80.715449317999983, 35.10204581111207 ], [ -80.715672184999974, 35.10195407811198 ], [ -80.716676184999983, 35.102663278112118 ], [ -80.716955384999949, 35.103144078112074 ], [ -80.718209917999957, 35.103418011112062 ], [ -80.719295717999955, 35.102455211112044 ], [ -80.719796517999953, 35.101790611112065 ], [ -80.720158384999948, 35.101469611112087 ], [ -80.720576317999985, 35.101286078112011 ], [ -80.721774584999935, 35.101193478112144 ], [ -80.722778117999951, 35.101376011112038 ], [ -80.723642317999975, 35.10162721111206 ], [ -80.72411651799996, 35.101993278112062 ], [ -80.724479517999953, 35.102565611111984 ], [ -80.724981718, 35.102977411111993 ], [ -80.725790317999966, 35.103251678112017 ], [ -80.726514917999964, 35.103251078112073 ], [ -80.727796384999976, 35.102883478112076 ], [ -80.728855317999944, 35.102722278112104 ], [ -80.72977418499994, 35.102240478112023 ], [ -80.730944384999972, 35.101918811112107 ], [ -80.731306384999982, 35.101643678112048 ], [ -80.731918117999967, 35.100566678112067 ], [ -80.73244731799997, 35.100383011112044 ], [ -80.732837384999982, 35.100336811112086 ], [ -80.733015096420075, 35.100378363988362 ] ] } }, +{ "type": "Feature", "id": "fp-19", "properties": { "length_km": 3.476, "slope_percent": 0.00834335, "main_id": 58, "toid": "fp-116", "realized_catchment": "cat-19" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.736650584999964, 35.259380211110354 ], [ -80.737627317999966, 35.258944078110389 ], [ -80.738576385, 35.258759878110361 ], [ -80.740112184999987, 35.25873567811032 ], [ -80.741089784999986, 35.259009478110315 ], [ -80.741732117999959, 35.259008811110277 ], [ -80.742988184999959, 35.258732811110313 ], [ -80.745332517999955, 35.257951678110388 ], [ -80.746030517999955, 35.257813611110329 ], [ -80.746896517999971, 35.258110611110268 ], [ -80.749214517999988, 35.258405878110359 ], [ -80.749800384999944, 35.258130478110324 ], [ -80.751515984999969, 35.257466478110366 ], [ -80.75210311799998, 35.256963211110282 ], [ -80.752857717999973, 35.256483011110369 ], [ -80.753556917999973, 35.255750811110353 ], [ -80.754117117999954, 35.25453741111037 ], [ -80.754732984999976, 35.25350741111037 ], [ -80.755235984999942, 35.253164278110404 ], [ -80.757053184999975, 35.251517011110458 ], [ -80.75750071799996, 35.250944878110488 ], [ -80.757863717999953, 35.25013961111042 ], [ -80.758060184999977, 35.249361078110404 ], [ -80.758535584999933, 35.248903278110369 ], [ -80.758647184999973, 35.24888061111043 ], [ -80.760604784999941, 35.246637878110469 ], [ -80.761107917999936, 35.246203211110497 ], [ -80.761862317999956, 35.24581461111049 ], [ -80.764069117999952, 35.244969211110451 ], [ -80.765102917999954, 35.24435167811049 ], [ -80.765662117999966, 35.243779611110469 ] ] } }, +{ "type": "Feature", "id": "fp-20", "properties": { "length_km": 2.724, "slope_percent": 0.00586863, "main_id": 54, "toid": "fp-125", "realized_catchment": "cat-20" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.767635917999925, 35.186843411111056 ], [ -80.767915917999957, 35.186087878111081 ], [ -80.768447184999957, 35.185034811111116 ], [ -80.768754717999983, 35.184622811111154 ], [ -80.76962051799994, 35.183799011111191 ], [ -80.769928184999955, 35.183157878111167 ], [ -80.771325117999922, 35.181601678111157 ], [ -80.772107184999982, 35.180823611111201 ], [ -80.772583184999959, 35.179449811111198 ], [ -80.772751517999936, 35.178717078111241 ], [ -80.773673384999938, 35.177641278111246 ], [ -80.774009384999957, 35.176588011111221 ], [ -80.774400584999981, 35.176130278111259 ], [ -80.776270184999987, 35.175605011111301 ], [ -80.778112517999944, 35.174530078111211 ], [ -80.778810517999943, 35.173980878111308 ], [ -80.779954585, 35.173569611111269 ], [ -80.781434117999936, 35.17260881111131 ], [ -80.781880917999956, 35.172173878111316 ], [ -80.783471517999942, 35.171648278111348 ], [ -80.783806584999979, 35.171282078111275 ], [ -80.785090584999978, 35.170504278111309 ], [ -80.785843984999985, 35.17023007811131 ], [ -80.78707131799996, 35.170185211111281 ] ] } }, +{ "type": "Feature", "id": "fp-21", "properties": { "length_km": 4.7968971139398358, "slope_percent": 0.000388915, "main_id": 12, "toid": "fp-31", "realized_catchment": "cat-21" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.877380137153864, 35.099382551446645 ], [ -80.877665784999976, 35.098687611112041 ], [ -80.877693584999975, 35.097702811112185 ], [ -80.878250984999966, 35.095641411112105 ], [ -80.879059117999986, 35.093900678112128 ], [ -80.881566917999976, 35.09055661111222 ], [ -80.881817717999979, 35.090465011112229 ], [ -80.88268151799997, 35.090396211112157 ], [ -80.883266584999959, 35.089938011112146 ], [ -80.883378117999939, 35.089754811112144 ], [ -80.883294317999969, 35.088403411112168 ], [ -80.883294184999968, 35.086823078112261 ], [ -80.883071184999949, 35.086090211112307 ], [ -80.880981184999939, 35.083754078112285 ], [ -80.880563184999971, 35.083090011112333 ], [ -80.880563117999941, 35.082082211112336 ], [ -80.881426717999958, 35.080066611112301 ], [ -80.882373984999958, 35.079425211112316 ], [ -80.882847784999967, 35.079333611112247 ], [ -80.883432917999983, 35.079562478112329 ], [ -80.884965317999956, 35.079631078112321 ], [ -80.886358384999937, 35.079401878112293 ], [ -80.886859917999971, 35.079195678112306 ], [ -80.887333517999934, 35.078829278112359 ], [ -80.887472784999957, 35.078577278112391 ], [ -80.887444784999957, 35.077935878112328 ], [ -80.887138317999984, 35.077752811112298 ], [ -80.885410784999976, 35.077317811112337 ], [ -80.885048584999936, 35.077111678112267 ], [ -80.884881384999986, 35.07685967811237 ], [ -80.88502058499995, 35.076172611112348 ], [ -80.885549918, 35.075370878112359 ], [ -80.886107117999984, 35.07507307811241 ], [ -80.88658058499999, 35.074958478112357 ], [ -80.887054317999969, 35.075233278112329 ], [ -80.887667517999944, 35.076286878112363 ], [ -80.888057517999982, 35.076607411112334 ], [ -80.889980117999983, 35.076836211112386 ], [ -80.890453584999989, 35.076790411112285 ], [ -80.891122185, 35.076515411112346 ], [ -80.892013784999975, 35.075965611112288 ], [ -80.892208584999935, 35.075599078112297 ], [ -80.892291917999955, 35.07429347811231 ], [ -80.892514784999932, 35.074087278112351 ], [ -80.893406317999961, 35.0738810781124 ], [ -80.893573384999968, 35.073697811112311 ], [ -80.893656917999976, 35.073262611112369 ], [ -80.89337818499996, 35.072965011112366 ], [ -80.893099517999985, 35.072781811112499 ], [ -80.892793117999986, 35.072736011112404 ] ] } }, +{ "type": "Feature", "id": "fp-22", "properties": { "length_km": 2.654, "slope_percent": 0.00059031333333333335, "main_id": 1, "toid": "fp-129", "realized_catchment": "cat-22" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.909458584999982, 35.017922678112981 ], [ -80.909653384999956, 35.017602011113013 ], [ -80.909513784999945, 35.016708678113012 ], [ -80.90890111799996, 35.016342411113008 ], [ -80.908678384999988, 35.016090611112993 ], [ -80.908399584999984, 35.015174478112975 ], [ -80.90759191799998, 35.014441678113023 ], [ -80.907480384999971, 35.014006611113039 ], [ -80.90739678499996, 35.013960878113011 ], [ -80.907173917999955, 35.013296611113041 ], [ -80.90670051799998, 35.012930411113054 ], [ -80.906032184999958, 35.012564011113014 ], [ -80.905419517999974, 35.011968611113041 ], [ -80.905196584999942, 35.011579411113097 ], [ -80.905279784999934, 35.010411211113031 ], [ -80.904861984999968, 35.009609611113078 ], [ -80.904304984999982, 35.009197478113187 ], [ -80.90335838499999, 35.00885421111311 ], [ -80.90296878499997, 35.009243611113114 ], [ -80.902940984999987, 35.009633011113088 ], [ -80.903163917999962, 35.010091078113149 ], [ -80.903581584999984, 35.010503211113118 ], [ -80.903693117999978, 35.010823878113044 ], [ -80.903553984999974, 35.011121611113097 ], [ -80.902218117999951, 35.012267211113084 ], [ -80.901744984999922, 35.012839878113034 ], [ -80.901577918, 35.012885678113065 ], [ -80.901494517999936, 35.013069011113075 ], [ -80.900798717999976, 35.013687611113056 ], [ -80.899740984999951, 35.014352078113014 ], [ -80.899184317999982, 35.014489611112928 ], [ -80.898571784999945, 35.014489678113058 ], [ -80.897708584999975, 35.014237878113072 ], [ -80.896928985, 35.013780011113035 ], [ -80.89690111799996, 35.013550878112994 ], [ -80.89748558499997, 35.013184411113031 ], [ -80.899211317999956, 35.011764011113009 ], [ -80.899350384999948, 35.011741011113102 ] ] } }, +{ "type": "Feature", "id": "fp-23", "properties": { "length_km": 2.578, "slope_percent": 0.00057796, "main_id": 1, "toid": "fp-22", "realized_catchment": "cat-23" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.897435584999982, 35.034164678112738 ], [ -80.897518917999946, 35.033386011112803 ], [ -80.898854784999969, 35.030728811112802 ], [ -80.898854584999967, 35.029995811112848 ], [ -80.89877098499997, 35.029927078112884 ], [ -80.898770784999954, 35.029377411112904 ], [ -80.89932751799995, 35.028713078112879 ], [ -80.899856317999934, 35.027888478112892 ], [ -80.900329584999952, 35.027407411112904 ], [ -80.900524385, 35.027040878112835 ], [ -80.900579917999934, 35.026468211112864 ], [ -80.900857984999959, 35.025620611112927 ], [ -80.901442317999965, 35.024269211112909 ], [ -80.902527917999976, 35.022871678112907 ], [ -80.902555517999986, 35.022367878112867 ], [ -80.902722384999947, 35.022024278112902 ], [ -80.903362717999983, 35.021405678112892 ], [ -80.903446117999962, 35.020970478112936 ], [ -80.903334517999951, 35.020558278113022 ], [ -80.90294451799997, 35.01989421111297 ], [ -80.902999984999951, 35.019436011112909 ], [ -80.903250384999978, 35.018817611113008 ], [ -80.903612185, 35.018725878112946 ], [ -80.904002184999968, 35.019206678113022 ], [ -80.90519938499996, 35.019046211112993 ], [ -80.905644784999936, 35.01888567811303 ], [ -80.906034384999941, 35.018633611112939 ], [ -80.906368517999965, 35.018564811113038 ], [ -80.906702717999977, 35.018702211112995 ], [ -80.907538117999934, 35.019274611112976 ], [ -80.907844518000019, 35.019320278112964 ], [ -80.908122784999989, 35.019091078113043 ], [ -80.908679317999955, 35.018312278112951 ], [ -80.909458584999982, 35.017922678112981 ] ] } }, +{ "type": "Feature", "id": "fp-24", "properties": { "length_km": 2.585, "slope_percent": 0.00085106, "main_id": 8, "toid": "fp-77", "realized_catchment": "cat-24" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.853762784999972, 35.067880078112474 ], [ -80.85484951799998, 35.066895478112386 ], [ -80.855100184999969, 35.066758078112386 ], [ -80.85574098499994, 35.066712411112519 ], [ -80.856047517999968, 35.066781078112491 ], [ -80.856548918000016, 35.0667124781124 ], [ -80.857022517999951, 35.066460611112497 ], [ -80.857802584999945, 35.065888078112486 ], [ -80.858582917999982, 35.0650636111125 ], [ -80.859084517999989, 35.063666611112573 ], [ -80.859363117999976, 35.063368878112577 ], [ -80.859976117999977, 35.06325441111256 ], [ -80.860700384999959, 35.062819278112435 ], [ -80.86078411799997, 35.062315411112401 ], [ -80.86047771799997, 35.0618344111125 ], [ -80.861174184999982, 35.061307678112499 ], [ -80.861759184999983, 35.061239011112519 ], [ -80.861982117999958, 35.061147478112488 ], [ -80.862149184999964, 35.060895611112535 ], [ -80.862455584999964, 35.060849678112511 ], [ -80.862706317999923, 35.060964278112529 ], [ -80.86309631799999, 35.060987211112561 ], [ -80.863319184999966, 35.060895611112535 ], [ -80.863597784999953, 35.060529211112588 ], [ -80.863932117999965, 35.060506278112562 ], [ -80.864015584999947, 35.060597878112461 ], [ -80.863876184999981, 35.061285078112526 ], [ -80.863792584999942, 35.061330878112472 ], [ -80.863792517999983, 35.061857678112503 ], [ -80.864210384999978, 35.062086678112472 ], [ -80.865240984999971, 35.062270011112481 ], [ -80.865881717999969, 35.062705278112503 ], [ -80.867413784999954, 35.062797011112465 ], [ -80.86813811799999, 35.062453411112458 ], [ -80.868528117999986, 35.062453411112458 ], [ -80.86866731799995, 35.062751211112491 ], [ -80.868611517999966, 35.06343827811245 ], [ -80.868444384999975, 35.063713211112542 ], [ -80.867915117999985, 35.064194078112521 ], [ -80.867720117999966, 35.064537611112456 ], [ -80.867664317999981, 35.065110278112471 ], [ -80.867859317999958, 35.065614211112567 ], [ -80.869224184999965, 35.066507478112477 ] ] } }, +{ "type": "Feature", "id": "fp-25", "properties": { "length_km": 1.495, "slope_percent": 0.00040802, "main_id": 8, "toid": "fp-24", "realized_catchment": "cat-25" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.84086151799994, 35.074679878112342 ], [ -80.841195784999968, 35.07468001111242 ], [ -80.84286771799998, 35.074199411112431 ], [ -80.84378718499994, 35.073833078112351 ], [ -80.844623185000017, 35.073329478112399 ], [ -80.846545784999989, 35.0724596111124 ], [ -80.847716184999967, 35.07184147811239 ], [ -80.848273517999985, 35.071337678112386 ], [ -80.848357384999986, 35.070421478112415 ], [ -80.848636117999945, 35.070032211112391 ], [ -80.85069791799998, 35.0693682781124 ], [ -80.851505784999972, 35.069231078112416 ], [ -80.851784384999974, 35.069322678112393 ], [ -80.852425117999985, 35.06932287811243 ], [ -80.852731584999958, 35.069093878112412 ], [ -80.853261117999978, 35.068475611112426 ], [ -80.853762784999972, 35.067880078112474 ] ] } }, +{ "type": "Feature", "id": "fp-26", "properties": { "length_km": 1.88, "slope_percent": 0.00210638, "main_id": 24, "toid": "fp-105", "realized_catchment": "cat-26" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.793466317999957, 35.079238411112314 ], [ -80.797673585, 35.07910367811234 ], [ -80.798760517999938, 35.078898278112305 ], [ -80.799986784999987, 35.078303611112396 ], [ -80.800990184999989, 35.077983478112316 ], [ -80.802884917999961, 35.077801411112326 ], [ -80.803608985, 35.078191211112269 ], [ -80.804806717999952, 35.078581211112279 ], [ -80.807286317999967, 35.078765878112343 ], [ -80.809180984999955, 35.078560678112368 ], [ -80.809710517999989, 35.078377678112339 ], [ -80.810546917999943, 35.077714011112363 ], [ -80.811912584999959, 35.077073411112323 ], [ -80.812832384999979, 35.076501278112381 ] ] } }, +{ "type": "Feature", "id": "fp-27", "properties": { "length_km": 2.35, "slope_percent": 0.0006851, "main_id": 8, "toid": "fp-25", "realized_catchment": "cat-27" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.827868784999964, 35.091372611112114 ], [ -80.830042584999944, 35.091006878112204 ], [ -80.830767384999945, 35.090457478112256 ], [ -80.831352984999967, 35.08967901111221 ], [ -80.832440717999987, 35.087984478112254 ], [ -80.833165784999949, 35.086587611112236 ], [ -80.834225518, 35.085076278112261 ], [ -80.835006184999955, 35.084137411112273 ], [ -80.835619517999987, 35.083610878112239 ], [ -80.836511584999982, 35.08251167811234 ], [ -80.839298984999971, 35.079947411112293 ], [ -80.839355184999974, 35.07903121111233 ], [ -80.839021317999922, 35.078115011112281 ], [ -80.838993717999983, 35.077473611112325 ], [ -80.839188984999964, 35.076717878112426 ], [ -80.840387717999988, 35.075046211112387 ], [ -80.84086151799994, 35.074679878112342 ] ] } }, +{ "type": "Feature", "id": "fp-28", "properties": { "length_km": 1.438, "slope_percent": 0.00289986, "main_id": 8, "toid": "fp-107", "realized_catchment": "cat-28" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.73545418499998, 35.160823278111401 ], [ -80.735788784999954, 35.160754278111412 ], [ -80.736484784999959, 35.159791678111425 ], [ -80.738404384999967, 35.156354411111451 ], [ -80.739462584999956, 35.155162411111455 ], [ -80.74191138499998, 35.151426878111543 ], [ -80.742634517999988, 35.150029011111592 ], [ -80.742801184999962, 35.149570811111538 ] ] } }, +{ "type": "Feature", "id": "fp-29", "properties": { "length_km": 1.719, "slope_percent": 0.00287572, "main_id": 8, "toid": "fp-109", "realized_catchment": "cat-29" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.709319517999958, 35.213361811110858 ], [ -80.704432184999973, 35.209815011110841 ], [ -80.704208518999948, 35.209242478110987 ], [ -80.704207984999982, 35.208669878110932 ], [ -80.703759984999976, 35.207112678110953 ], [ -80.703676184999949, 35.207067011110944 ], [ -80.703591384999982, 35.205944811110889 ], [ -80.703758584999946, 35.205669811110958 ], [ -80.704147584999987, 35.203814411110926 ], [ -80.70428611899996, 35.202898211110963 ], [ -80.704174184999943, 35.202508878110912 ], [ -80.70412271899994, 35.202343078110957 ], [ -80.704537184999936, 35.200989811110965 ], [ -80.704421518999951, 35.200173678111021 ], [ -80.704422918999967, 35.199989278111019 ] ] } }, +{ "type": "Feature", "id": "fp-30", "properties": { "length_km": 4.495, "slope_percent": 0.00101668, "main_id": 1, "toid": "fp-23", "realized_catchment": "cat-30" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.903432984999938, 35.064969411112365 ], [ -80.90337711799998, 35.064373878112505 ], [ -80.90315398499996, 35.063984611112538 ], [ -80.903209584999956, 35.063434878112496 ], [ -80.903042317999962, 35.063114278112479 ], [ -80.90151018499995, 35.062702411112561 ], [ -80.900869317999934, 35.062358878112498 ], [ -80.900061318, 35.061717811112509 ], [ -80.899726917999942, 35.06123701111251 ], [ -80.899559584999963, 35.060801811112562 ], [ -80.899559384999975, 35.060091811112549 ], [ -80.899977117999981, 35.0593128781126 ], [ -80.900115984999957, 35.058648611112538 ], [ -80.900728517999923, 35.057251278112588 ], [ -80.901313184999935, 35.056770211112621 ], [ -80.901647317999959, 35.056174678112569 ], [ -80.902594317999956, 35.056105678112601 ], [ -80.903930984999988, 35.054891478112587 ], [ -80.904738584999961, 35.054685211112577 ], [ -80.905072917999973, 35.054479011112534 ], [ -80.905044717999942, 35.053471211112559 ], [ -80.904877384999963, 35.053127611112586 ], [ -80.90462658499996, 35.052944478112657 ], [ -80.90328978499997, 35.053059278112578 ], [ -80.903038984999966, 35.052876078112675 ], [ -80.903010984999966, 35.05267001111266 ], [ -80.903233784999941, 35.052372211112626 ], [ -80.903122184999972, 35.052005811112629 ], [ -80.903038717999948, 35.051960011112627 ], [ -80.903038517999946, 35.051433211112588 ], [ -80.903288984999961, 35.050860478112646 ], [ -80.903845984999975, 35.050677211112692 ], [ -80.904764584999953, 35.049165211112623 ], [ -80.905571984999938, 35.048638278112676 ], [ -80.906017317999954, 35.047676211112666 ], [ -80.905989184999953, 35.04719521111263 ], [ -80.905822117999946, 35.046943278112686 ], [ -80.903983917999938, 35.046623078112773 ], [ -80.902813784999978, 35.045730078112697 ], [ -80.902117184999952, 35.044607878112735 ], [ -80.901838384999962, 35.043943811112719 ], [ -80.901448317999936, 35.043440011112644 ], [ -80.900640517999989, 35.042753011112715 ], [ -80.900306117999946, 35.042226278112707 ], [ -80.900277984999946, 35.041493411112789 ], [ -80.900138717999951, 35.041172878112747 ], [ -80.899247384999967, 35.040577478112745 ], [ -80.898383784999965, 35.039638611112714 ], [ -80.898105184999963, 35.03915767811273 ], [ -80.898104917999945, 35.038401811112763 ], [ -80.898327384999959, 35.037050478112818 ], [ -80.898160317999952, 35.036729811112757 ], [ -80.897436117999973, 35.035997078112771 ], [ -80.897435917999985, 35.035584811112777 ], [ -80.897769917999938, 35.034622678112825 ], [ -80.897435584999982, 35.034164678112738 ] ] } }, +{ "type": "Feature", "id": "fp-31", "properties": { "length_km": 3.856, "slope_percent": 0.00184906, "main_id": 12, "toid": "fp-30", "realized_catchment": "cat-31" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.892793117999986, 35.072736011112404 ], [ -80.892403117999947, 35.072988011112379 ], [ -80.891567317999943, 35.073148411112356 ], [ -80.89020218499995, 35.073263211112319 ], [ -80.890062917999956, 35.073102878112302 ], [ -80.890229917999989, 35.072255411112408 ], [ -80.889895384999988, 35.071408011112368 ], [ -80.889505317999976, 35.071041611112385 ], [ -80.888892317999989, 35.0706294111124 ], [ -80.888195717999963, 35.069140811112341 ], [ -80.888111984999966, 35.068224611112413 ], [ -80.888446117999976, 35.067606078112419 ], [ -80.888668984999981, 35.067468678112448 ], [ -80.889031184999965, 35.067468611112488 ], [ -80.889839117999927, 35.06781207811246 ], [ -80.889978384999964, 35.06801821111241 ], [ -80.890090118, 35.068980211112418 ], [ -80.890285117999952, 35.069163411112456 ], [ -80.891705917999957, 35.069392211112387 ], [ -80.892262984999945, 35.069163011112401 ], [ -80.892987384999955, 35.069071278112453 ], [ -80.89334951799999, 35.069185811112412 ], [ -80.893683917999965, 35.069460611112433 ], [ -80.893878984999958, 35.069804078112369 ], [ -80.894102117999964, 35.07078887811241 ], [ -80.894380717999979, 35.071017878112407 ], [ -80.896999584999961, 35.071132011112418 ], [ -80.897110984999983, 35.071063278112476 ], [ -80.897305917999972, 35.070628078112428 ], [ -80.897361184999966, 35.06929961111242 ], [ -80.896943117999967, 35.068429411112461 ], [ -80.896497184999987, 35.067902678112432 ], [ -80.895744917999934, 35.067307211112521 ], [ -80.895744984999965, 35.067055278112498 ], [ -80.895967717999952, 35.066780411112418 ], [ -80.89641338499996, 35.066711678112441 ], [ -80.896664118, 35.066803211112429 ], [ -80.896887117999938, 35.067559011112408 ], [ -80.897221517999981, 35.067856678112399 ], [ -80.89758358499995, 35.06787961111241 ], [ -80.898140917999967, 35.067787811112453 ], [ -80.899115784999935, 35.067306611112443 ], [ -80.89997918499995, 35.066458878112442 ], [ -80.900870517999977, 35.066115211112475 ], [ -80.901650517999954, 35.066092078112455 ], [ -80.902040517999978, 35.066206611112491 ], [ -80.902653785, 35.067168411112462 ], [ -80.902988117999939, 35.067374478112548 ], [ -80.903517385, 35.067397211112464 ], [ -80.903907384999954, 35.067191011112534 ], [ -80.90426931799999, 35.066870211112381 ], [ -80.90443638499994, 35.066480811112477 ], [ -80.903489184999941, 35.066389411112453 ], [ -80.903349785, 35.066137611112453 ], [ -80.903432984999938, 35.064969411112365 ] ] } }, +{ "type": "Feature", "id": "fp-32", "properties": { "length_km": 4.9495456949399266, "slope_percent": 0.0020890533333333332, "main_id": 12, "toid": "fp-111", "realized_catchment": "cat-32" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.843995024567377, 35.189094944254023 ], [ -80.844244518, 35.188695811111103 ], [ -80.844607584999949, 35.187734011111075 ], [ -80.845306117999939, 35.18484827811114 ], [ -80.845753317999936, 35.182626811111085 ], [ -80.845977184999981, 35.18065707811121 ], [ -80.846284584999978, 35.179168478111215 ], [ -80.846368584999937, 35.178481411111214 ], [ -80.846648917999971, 35.174335878111286 ], [ -80.84656551799999, 35.173534278111319 ], [ -80.84623131799998, 35.172205678111354 ], [ -80.84545058499998, 35.171358078111346 ], [ -80.845394917999982, 35.17103747811128 ], [ -80.846232118, 35.169915411111361 ], [ -80.846706384999948, 35.169686478111302 ], [ -80.846901784999943, 35.169388678111275 ], [ -80.846929784999986, 35.168655678111293 ], [ -80.846734784999967, 35.168243611111357 ], [ -80.846483784999975, 35.168014411111336 ], [ -80.844949984999971, 35.167556011111309 ], [ -80.844894384999975, 35.16716667811135 ], [ -80.845843184999978, 35.165930078111373 ], [ -80.846512784999945, 35.165197278111357 ], [ -80.847572917999969, 35.164670678111385 ], [ -80.84782398499992, 35.164441678111444 ], [ -80.847656917999984, 35.163456811111459 ], [ -80.847461917999965, 35.162998678111364 ], [ -80.847378184999982, 35.162952811111388 ], [ -80.847378318, 35.162769611111372 ], [ -80.847629384999948, 35.162494811111323 ], [ -80.84807591799995, 35.161693278111422 ], [ -80.84815978499995, 35.16114361111147 ], [ -80.848355184999946, 35.160685611111525 ], [ -80.848606184999952, 35.160479478111398 ], [ -80.849024717999939, 35.16031927811148 ], [ -80.849666117999959, 35.160227811111433 ], [ -80.849972984999965, 35.160113411111418 ], [ -80.850196117999985, 35.159930211111387 ], [ -80.850698184999956, 35.159815811111372 ], [ -80.850893384999949, 35.159632611111377 ], [ -80.851172917999975, 35.157823278111465 ], [ -80.851172984999948, 35.157342278111443 ], [ -80.851675184999962, 35.156678211111462 ], [ -80.85251198499999, 35.156105811111424 ], [ -80.854269384999981, 35.154319478111447 ], [ -80.854548384999987, 35.154113411111425 ], [ -80.855134184999955, 35.153334811111407 ], [ -80.855162184999983, 35.152808011111482 ], [ -80.85544131799999, 35.152029278111563 ], [ -80.856026917999941, 35.151708811111448 ], [ -80.857170384999961, 35.15131961111156 ] ] } }, +{ "type": "Feature", "id": "fp-33", "properties": { "length_km": 3.162, "slope_percent": 0.001566585, "main_id": 1, "toid": "fp-30", "realized_catchment": "cat-33" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.90107158499994, 35.086293611112268 ], [ -80.901990984999955, 35.085537611112208 ], [ -80.903607117999968, 35.08553721111231 ], [ -80.904108517999987, 35.085353811112199 ], [ -80.90447071799997, 35.085056011112172 ], [ -80.90466571799999, 35.084689478112253 ], [ -80.904860717999952, 35.084529078112197 ], [ -80.905055584999943, 35.084185611112261 ], [ -80.906307784999953, 35.079787611112302 ], [ -80.906335517999935, 35.079215011112346 ], [ -80.906223784999966, 35.078573678112257 ], [ -80.906418117999976, 35.076947478112444 ], [ -80.906473584999958, 35.076168678112353 ], [ -80.906361917999945, 35.075504611112315 ], [ -80.906501117999937, 35.075092278112372 ], [ -80.90650098499998, 35.074725811112422 ], [ -80.906389384999969, 35.07445101111243 ], [ -80.906082784999981, 35.074244878112324 ], [ -80.906054917999953, 35.074015878112384 ], [ -80.906194117999974, 35.073809678112362 ], [ -80.906138317999989, 35.073626478112324 ], [ -80.905692384999966, 35.073237211112357 ], [ -80.905859384999971, 35.072985211112382 ], [ -80.905803517999971, 35.072412678112421 ], [ -80.906276984999977, 35.072023078112466 ], [ -80.906471917999966, 35.071725411112453 ], [ -80.90655531799996, 35.071129811112378 ], [ -80.906833784999961, 35.070557211112394 ], [ -80.907390717999988, 35.070076078112464 ], [ -80.907362584999987, 35.069526411112442 ], [ -80.907223184999964, 35.069205811112411 ], [ -80.906610184999977, 35.068816611112432 ], [ -80.906498384999963, 35.068083611112378 ], [ -80.90694371799998, 35.066938278112453 ], [ -80.906999117999987, 35.065861811112455 ], [ -80.906608517999985, 35.06462501111254 ], [ -80.906274117999942, 35.064121211112521 ], [ -80.905772584999966, 35.06396107811252 ], [ -80.905271184999947, 35.064121478112469 ], [ -80.904937117999964, 35.064396478112407 ], [ -80.90412938499999, 35.064648611112446 ], [ -80.903878717999987, 35.064854811112554 ], [ -80.903432984999938, 35.064969411112365 ] ] } }, +{ "type": "Feature", "id": "fp-34", "properties": { "length_km": 3.702, "slope_percent": 0.00133981, "main_id": 1, "toid": "fp-33", "realized_catchment": "cat-34" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.89567171799996, 35.109794211112018 ], [ -80.895699517999944, 35.109679678111938 ], [ -80.895532317999937, 35.109542278111959 ], [ -80.895504317999951, 35.108901011111975 ], [ -80.895643517999986, 35.108534411111933 ], [ -80.895949984999973, 35.108259611111954 ], [ -80.896367784999939, 35.107205878112019 ], [ -80.896590717999956, 35.106999678112011 ], [ -80.897343117999981, 35.106724811112045 ], [ -80.898206784999942, 35.105419078112035 ], [ -80.898234584999955, 35.105213011112035 ], [ -80.89809531799996, 35.105006811112098 ], [ -80.898095185, 35.104800678111999 ], [ -80.898318117999963, 35.10454867811201 ], [ -80.899126184999929, 35.104182011111959 ], [ -80.89923758499998, 35.103746811112046 ], [ -80.899488317999953, 35.10338027811207 ], [ -80.899850584999967, 35.103219878112029 ], [ -80.90090971799998, 35.103288411112032 ], [ -80.901494784999954, 35.103013411112023 ], [ -80.901856917999964, 35.102234611112088 ], [ -80.901828784999964, 35.101662011112083 ], [ -80.901605717999985, 35.101226878111973 ], [ -80.901410584999951, 35.101089478112115 ], [ -80.900741717999949, 35.100998011112097 ], [ -80.900184384999989, 35.101158411112074 ], [ -80.899208917999943, 35.10115867811205 ], [ -80.899208784999985, 35.100883878112136 ], [ -80.899459517999958, 35.100150878112103 ], [ -80.898511784999982, 35.09969301111208 ], [ -80.898400184999957, 35.099326611112062 ], [ -80.898455917999968, 35.098937211112123 ], [ -80.898567384999978, 35.098845611112033 ], [ -80.899347717999945, 35.098662078112113 ], [ -80.899960717999974, 35.098295478112156 ], [ -80.900211384999977, 35.098020611112041 ], [ -80.901047117999951, 35.096783611112095 ], [ -80.901046917999963, 35.096165211112137 ], [ -80.900935317999938, 35.09598201111212 ], [ -80.900433717999988, 35.095798811112118 ], [ -80.899430517999974, 35.095707478112146 ], [ -80.899068117999946, 35.095364078112162 ], [ -80.898733517999972, 35.095180878112131 ], [ -80.898343317999945, 35.094814478112141 ], [ -80.898036317999981, 35.092982278112196 ], [ -80.89828698499997, 35.092730278112192 ], [ -80.898286784999982, 35.092020278112159 ], [ -80.898983317999978, 35.091378811112186 ], [ -80.899651717999987, 35.090095878112145 ], [ -80.899651517999985, 35.089317211112196 ], [ -80.899093717999961, 35.087966078112274 ], [ -80.898954184999965, 35.087324811112268 ], [ -80.898870584999941, 35.087256078112205 ], [ -80.898842717999969, 35.086866678112216 ], [ -80.89900978499999, 35.086637678112247 ], [ -80.899371984999959, 35.086477211112246 ], [ -80.90107158499994, 35.086293611112268 ] ] } }, +{ "type": "Feature", "id": "fp-35", "properties": { "length_km": 1.257, "slope_percent": 0.00353221, "main_id": 42, "toid": "fp-107", "realized_catchment": "cat-35" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.731029784999976, 35.146764411111548 ], [ -80.731754984999952, 35.146855411111517 ], [ -80.732926117999966, 35.146854278111533 ], [ -80.73342818499999, 35.14694547811159 ], [ -80.733902584999953, 35.147311478111618 ], [ -80.734711384999969, 35.147402411111571 ], [ -80.734823117999937, 35.147562678111569 ], [ -80.735019984999951, 35.148822211111543 ], [ -80.735773517999974, 35.149233678111578 ], [ -80.736358917999951, 35.149118678111492 ], [ -80.737000584999976, 35.149347211111582 ], [ -80.738143717999932, 35.149162878111582 ], [ -80.740095917999952, 35.149390078111487 ], [ -80.741490784999954, 35.149824011111612 ], [ -80.741964784999936, 35.149777678111505 ], [ -80.742522317999956, 35.149571078111549 ], [ -80.742801184999962, 35.149570811111538 ] ] } }, +{ "type": "Feature", "id": "fp-36", "properties": { "length_km": 1.181, "slope_percent": 0.00839147, "main_id": 73, "toid": "fp-49", "realized_catchment": "cat-36" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.833328384999959, 35.04139787811269 ], [ -80.833773784999948, 35.041993478112779 ], [ -80.834358384999973, 35.042268611112675 ], [ -80.835806384999955, 35.042635478112693 ], [ -80.836697384999979, 35.04323127811265 ], [ -80.838200917999984, 35.043918878112684 ], [ -80.839258984999958, 35.044583478112656 ], [ -80.839537184999983, 35.045064478112678 ], [ -80.839703984999971, 35.045660011112687 ], [ -80.839675717999981, 35.046805211112712 ], [ -80.839981584999975, 35.04788181111266 ], [ -80.840065117999984, 35.047950611112711 ], [ -80.840176184999962, 35.048660478112645 ], [ -80.840454584999975, 35.049072878112675 ] ] } }, +{ "type": "Feature", "id": "fp-37", "properties": { "length_km": 2.663, "slope_percent": 0.01248706, "main_id": 64, "toid": "fp-48", "realized_catchment": "cat-37" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.818117117999975, 35.049522811112681 ], [ -80.81878498499999, 35.050301811112618 ], [ -80.820009784999982, 35.051310211112657 ], [ -80.820427318, 35.051814278112602 ], [ -80.820565917999943, 35.052822078112541 ], [ -80.821456185, 35.054173878112614 ], [ -80.821678517999956, 35.055021411112627 ], [ -80.821817717999977, 35.055227611112592 ], [ -80.822067584999957, 35.056258411112594 ], [ -80.821899984999959, 35.05708287811256 ], [ -80.822010984999963, 35.05774721111257 ], [ -80.824182384999972, 35.05994681111256 ], [ -80.82515671799996, 35.060840478112553 ], [ -80.82562978499999, 35.061504811112499 ], [ -80.82579671799995, 35.06194021111255 ], [ -80.826325717999964, 35.06253581111249 ], [ -80.826492717999955, 35.062627611112482 ], [ -80.827133384999954, 35.062581878112475 ], [ -80.827579184999934, 35.06244467811252 ], [ -80.829835317999937, 35.062926478112423 ], [ -80.831255984999984, 35.063041611112503 ], [ -80.832759917999965, 35.063523011112522 ], [ -80.833901584999964, 35.064416678112444 ], [ -80.834402984999954, 35.064577211112528 ], [ -80.834765184999981, 35.064485678112455 ], [ -80.835266785000016, 35.064188078112529 ], [ -80.836158184999988, 35.064188411112497 ] ] } }, +{ "type": "Feature", "id": "fp-38", "properties": { "length_km": 0.893, "slope_percent": 0.00683139, "main_id": 75, "toid": "fp-105", "realized_catchment": "cat-38" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.808881317999976, 35.07020061111244 ], [ -80.812334117999967, 35.072378278112403 ], [ -80.812974517999976, 35.072882478112376 ], [ -80.813252717999958, 35.073340811112303 ], [ -80.813252317999968, 35.073959211112303 ], [ -80.812749317999987, 35.075905678112321 ], [ -80.812832384999979, 35.076501278112381 ] ] } }, +{ "type": "Feature", "id": "fp-39", "properties": { "length_km": 2.0, "slope_percent": 0.018814442, "main_id": 41, "toid": "fp-26", "realized_catchment": "cat-39" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.788798917999941, 35.066546478112478 ], [ -80.789575917999969, 35.069387078112449 ], [ -80.78957538499999, 35.069868078112492 ], [ -80.789240184999983, 35.070761211112355 ], [ -80.788263917999984, 35.071836878112428 ], [ -80.788162384999964, 35.071822678112397 ], [ -80.788153984999951, 35.071843811112402 ], [ -80.78807411799994, 35.07204387811236 ], [ -80.786742784999944, 35.072820678112407 ], [ -80.78657951799994, 35.073225411112375 ], [ -80.786562784999944, 35.073507678112364 ], [ -80.78667358499996, 35.074149078112448 ], [ -80.786561184999982, 35.075019278112407 ], [ -80.786588317999986, 35.075729411112341 ], [ -80.786726717999954, 35.076531078112318 ], [ -80.786921384999971, 35.076737411112347 ], [ -80.78742278499999, 35.076898011112448 ], [ -80.788760317999959, 35.07673867811247 ], [ -80.789094584999987, 35.07676181111227 ], [ -80.789874384999962, 35.077105878112377 ], [ -80.79059871799997, 35.077152278112308 ], [ -80.791155717999985, 35.077313011112309 ], [ -80.793077317999973, 35.078161678112295 ], [ -80.793494917999979, 35.078551278112272 ], [ -80.793466317999957, 35.079238411112314 ] ] } }, +{ "type": "Feature", "id": "fp-40", "properties": { "length_km": 0.986, "slope_percent": 0.015661035, "main_id": 77, "toid": "fp-27", "realized_catchment": "cat-40" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.818312517999971, 35.088757611112264 ], [ -80.822073184999965, 35.090477011112206 ], [ -80.82335491799995, 35.090821211112221 ], [ -80.824385917999976, 35.090707078112167 ], [ -80.825835317999974, 35.090226611112151 ], [ -80.826643317999981, 35.090387278112146 ], [ -80.827283784999963, 35.091074611112226 ], [ -80.827840984999966, 35.09128087811218 ], [ -80.827868784999964, 35.091372611112114 ] ] } }, +{ "type": "Feature", "id": "fp-41", "properties": { "length_km": 1.132, "slope_percent": 0.0160122, "main_id": 80, "toid": "fp-50", "realized_catchment": "cat-41" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.757912917999988, 35.119268611111849 ], [ -80.759083184999952, 35.119590411111858 ], [ -80.760588117999987, 35.119866478111909 ], [ -80.762065917999962, 35.119570278111816 ], [ -80.76295838499999, 35.119227478111895 ], [ -80.763516584999934, 35.11870121111199 ], [ -80.764074384999972, 35.118449811111873 ], [ -80.764687784999978, 35.118358811111882 ], [ -80.76560838499995, 35.117901478111868 ], [ -80.766529184999968, 35.117192278111872 ], [ -80.767617584999982, 35.116139611111876 ], [ -80.768176117999985, 35.115407211111901 ] ] } }, +{ "type": "Feature", "id": "fp-42", "properties": { "length_km": 1.447, "slope_percent": 0.01711864, "main_id": 47, "toid": "fp-52", "realized_catchment": "cat-42" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.690386718999932, 35.127072611111807 ], [ -80.690860518999955, 35.127003678111777 ], [ -80.692533318999949, 35.127071478111745 ], [ -80.694679918999952, 35.127322211111768 ], [ -80.695739584999956, 35.127550678111746 ], [ -80.696687718999954, 35.127939478111784 ], [ -80.697636184999965, 35.128717611111782 ], [ -80.697915118999958, 35.128809078111765 ], [ -80.699420718999988, 35.128876878111747 ], [ -80.700257184999941, 35.129128278111743 ], [ -80.70123351899997, 35.129654478111696 ], [ -80.70229371899994, 35.130570011111871 ], [ -80.703576718999955, 35.131233411111751 ], [ -80.704664384999944, 35.13169081111171 ] ] } }, +{ "type": "Feature", "id": "fp-43", "properties": { "length_km": 2.353, "slope_percent": 0.0071221, "main_id": 116, "toid": "fp-125", "realized_catchment": "cat-43" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.79017478499999, 35.189815678111067 ], [ -80.790314984999938, 35.189060011111067 ], [ -80.790147984999976, 35.188739211111134 ], [ -80.789535184999963, 35.18775387811116 ], [ -80.789340384999946, 35.187318678111133 ], [ -80.788643984999965, 35.186264611111078 ], [ -80.78761291799999, 35.185278878111134 ], [ -80.786859984999978, 35.184889078111162 ], [ -80.786386317999984, 35.184384811111258 ], [ -80.786247518, 35.183835078111215 ], [ -80.786193117999971, 35.182529478111107 ], [ -80.785720517999948, 35.181086211111158 ], [ -80.785721317999972, 35.180330411111171 ], [ -80.785945384999934, 35.179460211111142 ], [ -80.786673384999972, 35.176987211111296 ], [ -80.787120584999968, 35.176254611111233 ], [ -80.787093784999968, 35.175201011111291 ], [ -80.786815917999945, 35.174215878111241 ], [ -80.786816784999985, 35.173345611111273 ], [ -80.787209184999938, 35.171674011111342 ], [ -80.786986918, 35.170872078111266 ], [ -80.78707131799996, 35.170185211111281 ] ] } }, +{ "type": "Feature", "id": "fp-44", "properties": { "length_km": 0.831, "slope_percent": 0.011990385, "main_id": 79, "toid": "fp-88", "realized_catchment": "cat-44" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.837777384999981, 35.116822211111888 ], [ -80.837222184999973, 35.112012211111917 ], [ -80.837250184999945, 35.111599878111932 ], [ -80.838086584999985, 35.111302478111931 ], [ -80.838728184999979, 35.110180278111997 ], [ -80.838756184999966, 35.109974211111982 ] ] } }, +{ "type": "Feature", "id": "fp-45", "properties": { "length_km": 2.393, "slope_percent": 0.00782783, "main_id": 95, "toid": "fp-113", "realized_catchment": "cat-45" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.759193117999985, 35.218625811110734 ], [ -80.759526917999949, 35.21940481111077 ], [ -80.760279317999959, 35.22023007811076 ], [ -80.760725584999989, 35.220436678110786 ], [ -80.761534784999981, 35.220483078110725 ], [ -80.76170218499999, 35.220597678110728 ], [ -80.762092117999941, 35.22121647811074 ], [ -80.76251018499994, 35.221606278110748 ], [ -80.763235784999949, 35.221652811110751 ], [ -80.76365418499995, 35.221836411110765 ], [ -80.76393298499994, 35.222065678110717 ], [ -80.764769984999987, 35.222318411110713 ], [ -80.765550584999971, 35.222937478110723 ], [ -80.765884584999981, 35.223670678110743 ], [ -80.766525917999971, 35.224175211110648 ], [ -80.76705558499998, 35.224587878110704 ], [ -80.767417117999969, 35.22564167811074 ], [ -80.767639984999974, 35.225848078110609 ], [ -80.768393117999949, 35.226169411110639 ], [ -80.76856031799997, 35.226398611110653 ], [ -80.769368917999927, 35.227086278110669 ], [ -80.770288318, 35.22834681111064 ], [ -80.770343784999966, 35.228713278110696 ], [ -80.770427317999989, 35.228782078110683 ], [ -80.770787784999982, 35.230729211110599 ], [ -80.771038117999979, 35.231416478110638 ], [ -80.771623717999944, 35.23180627811066 ], [ -80.773577517999968, 35.23194541111058 ], [ -80.77382858499999, 35.232083078110634 ], [ -80.773800517999973, 35.232266278110664 ], [ -80.77360491799999, 35.232426478110597 ], [ -80.773604717999945, 35.232563878110589 ], [ -80.773911384999963, 35.2328160111106 ], [ -80.774050584999941, 35.233205611110648 ], [ -80.774049917999946, 35.233663611110572 ] ] } }, +{ "type": "Feature", "id": "fp-46", "properties": { "length_km": 2.223, "slope_percent": 0.01336065, "main_id": 92, "toid": "fp-111", "realized_catchment": "cat-46" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.875631184999975, 35.160184411111437 ], [ -80.874794517999987, 35.160528078111412 ], [ -80.87437618499996, 35.16068841111143 ], [ -80.873539517999973, 35.160665478111433 ], [ -80.873232717999954, 35.160482278111424 ], [ -80.872117184999979, 35.159245411111435 ], [ -80.871336317999976, 35.158627011111449 ], [ -80.870276584999942, 35.157504611111413 ], [ -80.869579384999952, 35.156519811111423 ], [ -80.868910184999947, 35.155901411111429 ], [ -80.868352517999938, 35.1556264781115 ], [ -80.867376517999958, 35.155420278111514 ], [ -80.866902384999946, 35.155237078111483 ], [ -80.865033984999982, 35.154778878111486 ], [ -80.863974317999975, 35.154435211111483 ], [ -80.862691584999936, 35.153679278111547 ], [ -80.861631784999986, 35.153908211111528 ], [ -80.860293184999989, 35.154137211111546 ], [ -80.859791184999949, 35.154137078111482 ], [ -80.858898917999966, 35.153449878111509 ], [ -80.85862018499995, 35.152991811111448 ], [ -80.858090384999969, 35.15257941111156 ], [ -80.858090517999983, 35.152304611111532 ], [ -80.858257917999936, 35.152029678111575 ], [ -80.858257917999936, 35.151732011111505 ], [ -80.857923317999962, 35.151617411111587 ], [ -80.857393518, 35.151594478111491 ], [ -80.857170384999961, 35.15131961111156 ] ] } }, +{ "type": "Feature", "id": "fp-47", "properties": { "length_km": 2.413, "slope_percent": 0.01436588, "main_id": 91, "toid": "fp-83", "realized_catchment": "cat-47" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.87415318499994, 35.153588211111533 ], [ -80.874013784999988, 35.153542478111511 ], [ -80.873288784999957, 35.152901078111505 ], [ -80.872842717999944, 35.15230561111153 ], [ -80.872591717999981, 35.151664278111525 ], [ -80.872535984999956, 35.149511278111547 ], [ -80.872982117999982, 35.148824211111553 ], [ -80.873010117999982, 35.148114211111604 ], [ -80.873233184999947, 35.147152211111568 ], [ -80.873205317999989, 35.14527421111157 ], [ -80.872898584999973, 35.14426641111158 ], [ -80.87270351799998, 35.142319478111659 ], [ -80.872591984999985, 35.142113478111661 ], [ -80.87144871799994, 35.14197601111163 ], [ -80.871169984999952, 35.142067611111635 ], [ -80.870918984999946, 35.142319478111659 ], [ -80.870417117999978, 35.142434011111575 ], [ -80.86893931799996, 35.142296478111646 ], [ -80.868297917999939, 35.141907211111572 ], [ -80.868102784999962, 35.141540678111653 ], [ -80.867851784999971, 35.141334611111539 ], [ -80.867266384999937, 35.141082611111614 ], [ -80.866792517999968, 35.140624478111704 ], [ -80.866680917999986, 35.14046421111162 ], [ -80.865565717999971, 35.139777011111555 ], [ -80.862944917999982, 35.138288011111648 ] ] } }, +{ "type": "Feature", "id": "fp-48", "properties": { "length_km": 1.932, "slope_percent": 0.00456799, "main_id": 64, "toid": "fp-24", "realized_catchment": "cat-48" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.836158184999988, 35.064188411112497 ], [ -80.837132984999982, 35.064509411112581 ], [ -80.838302917999968, 35.064647211112415 ], [ -80.841227917999959, 35.064579211112516 ], [ -80.841701584999953, 35.06435027811245 ], [ -80.843512917999988, 35.062816211112533 ], [ -80.844599517999939, 35.062495811112555 ], [ -80.845825117999937, 35.062702278112525 ], [ -80.849223317999986, 35.063756611112495 ], [ -80.84983598499997, 35.064146011112477 ], [ -80.850810717999948, 35.064925011112422 ], [ -80.851701917999947, 35.065978678112494 ], [ -80.852036117999944, 35.06611621111248 ], [ -80.852091784999971, 35.0664828111125 ], [ -80.853567784999925, 35.067811411112473 ], [ -80.853762784999972, 35.067880078112474 ] ] } }, +{ "type": "Feature", "id": "fp-49", "properties": { "length_km": 2.599, "slope_percent": 0.00899957, "main_id": 73, "toid": "fp-24", "realized_catchment": "cat-49" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.840454584999975, 35.049072878112675 ], [ -80.840482317999943, 35.049256211112649 ], [ -80.841428784999948, 35.050470278112599 ], [ -80.842096984999955, 35.051066011112646 ], [ -80.842681918, 35.051364011112582 ], [ -80.844018317999925, 35.052486678112636 ], [ -80.844213184999944, 35.052761478112579 ], [ -80.844240784999954, 35.053540278112628 ], [ -80.84513158499999, 35.054823078112619 ], [ -80.845381984999946, 35.055372878112621 ], [ -80.845632584999976, 35.055602011112626 ], [ -80.846412517999966, 35.055968611112561 ], [ -80.846969184999978, 35.056930678112536 ], [ -80.846996917999945, 35.057114011112596 ], [ -80.847219717999977, 35.057388878112555 ], [ -80.847442317999935, 35.058099011112574 ], [ -80.848221984999952, 35.058740478112526 ], [ -80.848193984999966, 35.05935881111246 ], [ -80.848555984999976, 35.059633811112569 ], [ -80.849252317999984, 35.059908678112471 ], [ -80.849837117999982, 35.060412678112449 ], [ -80.850895517999987, 35.060756411112564 ], [ -80.851285384999983, 35.06116881111253 ], [ -80.851480317999986, 35.061535278112508 ], [ -80.851953517999974, 35.06302421111257 ], [ -80.852565785, 35.064719211112489 ], [ -80.852537784999981, 35.065200078112476 ], [ -80.852036117999944, 35.06611621111248 ] ] } }, +{ "type": "Feature", "id": "fp-50", "properties": { "length_km": 2.696, "slope_percent": 0.00877225, "main_id": 80, "toid": "fp-81", "realized_catchment": "cat-50" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.768176117999985, 35.115407211111901 ], [ -80.768259917999941, 35.115269878111917 ], [ -80.769236317999969, 35.11465227811189 ], [ -80.770853717999955, 35.114149811111908 ], [ -80.771578918, 35.113784011111846 ], [ -80.77514711799995, 35.113420411111903 ], [ -80.776150918, 35.113054811111887 ], [ -80.777683584999963, 35.113422611111908 ], [ -80.778686917999948, 35.113514878111914 ], [ -80.778909784999939, 35.113606811112 ], [ -80.779355384999988, 35.113996411111955 ], [ -80.779633584999942, 35.114408878111924 ], [ -80.780162584999971, 35.114890278111908 ], [ -80.78041338499996, 35.1149820781119 ], [ -80.782057784999964, 35.115098011111904 ], [ -80.782698984999968, 35.115029611111943 ], [ -80.782866384999977, 35.114938078111933 ], [ -80.78297811799996, 35.114755011111988 ], [ -80.783229184999968, 35.114571878111953 ], [ -80.783452117999971, 35.11459507811189 ], [ -80.784343118, 35.115420278111863 ], [ -80.78501158499995, 35.115901678111882 ], [ -80.785429784999977, 35.115810411111894 ], [ -80.786015517999928, 35.115536011111928 ], [ -80.786740117999955, 35.115673878111899 ], [ -80.787241584999947, 35.115880478111904 ], [ -80.787938384999961, 35.115949678111825 ], [ -80.788300584999959, 35.116064411111957 ], [ -80.788829784999976, 35.116522878111923 ], [ -80.789107784999942, 35.117141478111961 ], [ -80.789330517999986, 35.11741647811192 ], [ -80.790250184999934, 35.117577411111803 ], [ -80.790445184999939, 35.117737878111882 ], [ -80.790695117999974, 35.118677078111823 ], [ -80.790694584999983, 35.119066411111881 ], [ -80.790917384999929, 35.119295678111932 ], [ -80.791892517999941, 35.119800211111844 ], [ -80.792310384999951, 35.119960811111866 ] ] } }, +{ "type": "Feature", "id": "fp-51", "properties": { "length_km": 1.533, "slope_percent": 1.0000000000000001e-005, "main_id": 47, "toid": "fp-35", "realized_catchment": "cat-51" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.72348711799998, 35.135112678111689 ], [ -80.723793784999941, 35.135158211111722 ], [ -80.724156517999972, 35.135295411111727 ], [ -80.724491317999949, 35.135547011111662 ], [ -80.724742717999959, 35.13595907811164 ], [ -80.724938584999961, 35.136554411111668 ], [ -80.725665184999968, 35.137928011111669 ], [ -80.726726784999983, 35.139599211111609 ], [ -80.726754784999969, 35.13975947811165 ], [ -80.727704584999969, 35.141155811111652 ], [ -80.727928317999954, 35.141728211111648 ], [ -80.728012984999964, 35.142529811111643 ], [ -80.728236317999958, 35.142781478111701 ], [ -80.728961784999967, 35.143170211111567 ], [ -80.729408384999942, 35.143513411111641 ], [ -80.729492318, 35.143742411111639 ], [ -80.729521317999968, 35.144544011111591 ], [ -80.729716784999937, 35.144864478111536 ], [ -80.730637984999987, 35.145688211111541 ], [ -80.730777784999987, 35.145917078111587 ], [ -80.731029784999976, 35.146764411111548 ] ] } }, +{ "type": "Feature", "id": "fp-52", "properties": { "length_km": 2.288, "slope_percent": 0.02440191, "main_id": 47, "toid": "fp-51", "realized_catchment": "cat-52" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.704664384999944, 35.13169081111171 ], [ -80.705082918999935, 35.131988211111732 ], [ -80.705668318999983, 35.131919078111743 ], [ -80.70614218499999, 35.13175847811172 ], [ -80.706225718999974, 35.131643878111753 ], [ -80.706225318999941, 35.13136907811171 ], [ -80.70591838499999, 35.13104867811176 ], [ -80.706001918999959, 35.130842478111795 ], [ -80.706642518999956, 35.130223611111802 ], [ -80.707338985, 35.129742211111747 ], [ -80.710265184999969, 35.128686611111746 ], [ -80.711686984999986, 35.128685678111793 ], [ -80.712802384999989, 35.129051278111817 ], [ -80.713249117999965, 35.129646478111766 ], [ -80.713834717999987, 35.129829278111735 ], [ -80.714030118, 35.130012411111757 ], [ -80.714058517999945, 35.130493278111778 ], [ -80.714393584999968, 35.130974078111741 ], [ -80.714505784999972, 35.131592411111697 ], [ -80.714673317999939, 35.131844211111698 ], [ -80.715259117999949, 35.132164411111773 ], [ -80.71537111799995, 35.132553678111755 ], [ -80.716041184999938, 35.133423611111716 ], [ -80.716655117999949, 35.1339498781117 ], [ -80.719499917999954, 35.134818078111707 ], [ -80.719669584999963, 35.134760611111695 ], [ -80.723107184999947, 35.135023078111637 ], [ -80.72348711799998, 35.135112678111689 ] ] } }, +{ "type": "Feature", "id": "fp-53", "properties": { "length_km": 0.318, "slope_percent": 1.0000000000000001e-005, "main_id": 18, "toid": "fp-31", "realized_catchment": "cat-53" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.893656917999976, 35.073262611112369 ], [ -80.893768317999957, 35.073216811112374 ], [ -80.894018984999946, 35.072735811112402 ], [ -80.894046717999956, 35.072254811112408 ], [ -80.893963117999988, 35.072117411112472 ], [ -80.893740184999956, 35.07198001111243 ], [ -80.893322317999946, 35.071957211112391 ], [ -80.892848717999982, 35.072140478112445 ], [ -80.892793117999986, 35.072736011112404 ] ] } }, +{ "type": "Feature", "id": "fp-54", "properties": { "length_km": 2.652, "slope_percent": 0.01153636, "main_id": 86, "toid": "fp-118", "realized_catchment": "cat-54" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.889422517999947, 35.244903678110489 ], [ -80.888640584999976, 35.244491478110469 ], [ -80.88788678499999, 35.243896211110503 ], [ -80.886909517999982, 35.242911411110562 ], [ -80.886127784999985, 35.242430478110521 ], [ -80.88509458499999, 35.241468678110522 ], [ -80.885038917999964, 35.241285411110503 ], [ -80.884815517999982, 35.241079411110519 ], [ -80.884061717999955, 35.240781611110478 ], [ -80.883782517999961, 35.240575611110557 ], [ -80.883112384999947, 35.239796878110575 ], [ -80.882526184999961, 35.238857878110579 ], [ -80.882051584999942, 35.238468478110576 ], [ -80.881604917999937, 35.237689878110579 ], [ -80.881576784999936, 35.237094478110514 ], [ -80.881744384999934, 35.236521878110537 ], [ -80.880683517999955, 35.235537078110696 ], [ -80.880320517999976, 35.234575078110552 ], [ -80.879873917999987, 35.234094211110595 ], [ -80.878533917999988, 35.233132211110558 ], [ -80.878338517999964, 35.232834478110611 ], [ -80.877640584999952, 35.232261878110592 ], [ -80.877082317999964, 35.232101678110688 ], [ -80.875882117999936, 35.231918411110655 ], [ -80.874793384999975, 35.231277211110601 ], [ -80.873927984999966, 35.23095661111067 ], [ -80.87236498499999, 35.230635811110631 ], [ -80.870494784999963, 35.230521278110636 ], [ -80.869796917999963, 35.230383878110658 ], [ -80.869238717999963, 35.230109011110692 ] ] } }, +{ "type": "Feature", "id": "fp-55", "properties": { "length_km": 0.254, "slope_percent": 0.00457142, "main_id": 4, "toid": "fp-129", "realized_catchment": "cat-55" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.901577918, 35.012885678113065 ], [ -80.900714984999979, 35.012840211113101 ], [ -80.90004658499997, 35.012450878113029 ], [ -80.899350384999948, 35.011741011113102 ] ] } }, +{ "type": "Feature", "id": "fp-56", "properties": { "length_km": 3.472, "slope_percent": 0.00489611, "main_id": 50, "toid": "fp-58", "realized_catchment": "cat-56" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.876638317999934, 35.290210811109993 ], [ -80.87663818499999, 35.289615278109956 ], [ -80.875436984999965, 35.287874678109993 ], [ -80.875269384999967, 35.287577011109967 ], [ -80.874794584999961, 35.28546987811005 ], [ -80.87451518499995, 35.285103411110079 ], [ -80.873537584999966, 35.284301811109977 ], [ -80.87247631799994, 35.2830422111101 ], [ -80.871303184999988, 35.282561211110021 ], [ -80.870548984999971, 35.281942811110042 ], [ -80.870297717999961, 35.281828211110053 ], [ -80.869711117999941, 35.281782411110065 ], [ -80.868928917999938, 35.282103011110081 ], [ -80.868482117999974, 35.282148811110112 ], [ -80.868118984999967, 35.281919678110064 ], [ -80.867783917999972, 35.281507478110044 ], [ -80.867672117999973, 35.281095278110101 ], [ -80.867588384999976, 35.281049411110118 ], [ -80.867588384999976, 35.280362278110069 ], [ -80.867756117999988, 35.279904278110074 ], [ -80.868063384999971, 35.279423278110158 ], [ -80.868230984999968, 35.278965211110133 ], [ -80.868119317999955, 35.278438411110109 ], [ -80.867309384999942, 35.277293278110093 ], [ -80.867114117999961, 35.27623967811018 ], [ -80.866723117999982, 35.275644078110162 ], [ -80.866276384999935, 35.274338611110224 ], [ -80.86560618499999, 35.273491211110134 ], [ -80.865103517999955, 35.27312467811015 ], [ -80.864824184999975, 35.272804011110175 ], [ -80.86471251799999, 35.271910811110203 ], [ -80.86471258499995, 35.271292411110224 ], [ -80.865550717999952, 35.269941211110243 ], [ -80.865913784999975, 35.268979278110237 ], [ -80.865913917999976, 35.268109011110205 ], [ -80.866528384999981, 35.266803478110269 ], [ -80.868064384999968, 35.265818678110257 ], [ -80.868985917999964, 35.265383478110188 ] ] } }, +{ "type": "Feature", "id": "fp-57", "properties": { "length_km": 1.613, "slope_percent": 0.01332194, "main_id": 42, "toid": "fp-122", "realized_catchment": "cat-57" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.660584984999943, 35.192817878110986 ], [ -80.66301258499999, 35.193366878110993 ], [ -80.66490971899999, 35.193297611111014 ], [ -80.666081584999958, 35.193388878111122 ], [ -80.668620118999968, 35.192609278111064 ], [ -80.669847584999957, 35.192448478111061 ], [ -80.670070784999936, 35.192311078111047 ], [ -80.670293784999956, 35.191990278111085 ], [ -80.670544518999989, 35.19134887811105 ], [ -80.670906118999966, 35.189195878111121 ], [ -80.671017584999973, 35.188966811111129 ], [ -80.671463784999958, 35.188623078111156 ], [ -80.673137184999973, 35.187889611111125 ], [ -80.674029584999985, 35.187225011111032 ] ] } }, +{ "type": "Feature", "id": "fp-58", "properties": { "length_km": 2.399, "slope_percent": 0.00328053, "main_id": 50, "toid": "fp-120", "realized_catchment": "cat-58" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.868985917999964, 35.265383478110188 ], [ -80.868929984999966, 35.265177411110272 ], [ -80.86867871799997, 35.264902611110223 ], [ -80.868706717999927, 35.264581878110349 ], [ -80.86893011799998, 35.264169678110299 ], [ -80.868958117999966, 35.26362001111027 ], [ -80.868511317999946, 35.263047411110293 ], [ -80.868511384999962, 35.262589411110326 ], [ -80.868008784999972, 35.261673078110299 ], [ -80.86806478499993, 35.260436411110348 ], [ -80.868623317999976, 35.259497278110295 ], [ -80.868651317999934, 35.259199611110304 ], [ -80.86839998499994, 35.258879011110345 ], [ -80.867841584999951, 35.258420878110329 ], [ -80.867450717999986, 35.25821467811037 ], [ -80.865831184999948, 35.2577106781103 ], [ -80.865384517999985, 35.257412878110379 ], [ -80.864993584999979, 35.256840411110346 ], [ -80.86490998499994, 35.255970011110286 ], [ -80.86502171799998, 35.255397478110346 ], [ -80.86502171799998, 35.254435478110416 ], [ -80.865161384999951, 35.253588078110347 ], [ -80.865859584999953, 35.252465878110399 ], [ -80.865803917999983, 35.251206211110436 ], [ -80.866111117999935, 35.250885611110398 ], [ -80.866725317999965, 35.250450411110471 ], [ -80.866947517999961, 35.250149278110385 ], [ -80.868148317999939, 35.248294278110485 ], [ -80.868622984999959, 35.247813278110449 ], [ -80.869795717999978, 35.247424011110439 ] ] } }, +{ "type": "Feature", "id": "fp-59", "properties": { "length_km": 1.791, "slope_percent": 0.00462522, "main_id": 123, "toid": "fp-116", "realized_catchment": "cat-59" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.749943517999952, 35.244268411110497 ], [ -80.750390717999935, 35.243994078110539 ], [ -80.751423917999944, 35.24378887811055 ], [ -80.752763517999952, 35.24401927811045 ], [ -80.753405984999986, 35.243768078110499 ], [ -80.753601917999944, 35.243401811110473 ], [ -80.754524984999989, 35.242280411110492 ], [ -80.755279584999982, 35.241662878110581 ], [ -80.757038784999963, 35.241389811110523 ], [ -80.757346118, 35.241229678110521 ], [ -80.757764917999964, 35.241207078110548 ], [ -80.759243984999955, 35.241575011110498 ], [ -80.760779384999978, 35.24162227811059 ], [ -80.761197984999967, 35.241760078110573 ], [ -80.761783517999959, 35.242241678110538 ], [ -80.762649117999956, 35.242150811110477 ], [ -80.763654717999927, 35.241762411110514 ], [ -80.763989717999934, 35.241739811110506 ], [ -80.764156917999969, 35.241946011110514 ], [ -80.764099384999952, 35.243205678110421 ], [ -80.764406184999984, 35.243435011110513 ], [ -80.76490851799997, 35.243641478110597 ], [ -80.765662117999966, 35.243779611110469 ] ] } }, +{ "type": "Feature", "id": "fp-60", "properties": { "length_km": 1.856, "slope_percent": 0.00336848, "main_id": 124, "toid": "fp-90", "realized_catchment": "cat-60" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.792952917999969, 35.257684278110339 ], [ -80.794098117999965, 35.257249878110315 ], [ -80.795885784999953, 35.256655478110417 ], [ -80.796556184999986, 35.256335411110342 ], [ -80.797757784999988, 35.255374211110336 ], [ -80.798512117999962, 35.254870811110358 ], [ -80.799461717999989, 35.254619411110333 ], [ -80.800104117999979, 35.254344878110366 ], [ -80.800523317999975, 35.253978678110386 ], [ -80.802451717999986, 35.251804078110446 ], [ -80.803038384999979, 35.251460878110358 ], [ -80.805830984999943, 35.250821211110413 ], [ -80.806640717999983, 35.250844478110366 ], [ -80.807785584999976, 35.250707678110473 ], [ -80.80932151799999, 35.25029627811044 ], [ -80.810353517999943, 35.250132878110342 ] ] } }, +{ "type": "Feature", "id": "fp-61", "properties": { "length_km": 1.459, "slope_percent": 0.01054198, "main_id": 12, "toid": "fp-67", "realized_catchment": "cat-61" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.797584984999972, 35.290622278110014 ], [ -80.798228384999973, 35.289637878110007 ], [ -80.798733184999961, 35.287508078110108 ], [ -80.798593917999938, 35.28702707811005 ], [ -80.798650784999964, 35.286019411110004 ], [ -80.798874584999965, 35.285607211110118 ], [ -80.798930784999982, 35.285309611110094 ], [ -80.799294384999953, 35.284691411109982 ], [ -80.799462117999951, 35.284554078110041 ], [ -80.799574784999947, 35.283546411110102 ], [ -80.799519784999973, 35.28265321111018 ], [ -80.799408317999962, 35.2823782111101 ], [ -80.798738717999981, 35.281644878110114 ], [ -80.798124917999985, 35.280820011110166 ], [ -80.798042184999986, 35.279697678110097 ], [ -80.798182117999943, 35.27937707811008 ], [ -80.799048717999938, 35.278530211110194 ] ] } }, +{ "type": "Feature", "id": "fp-62", "properties": { "length_km": 1.643, "slope_percent": 0.0061219, "main_id": 44, "toid": "fp-66", "realized_catchment": "cat-62" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.848198317999959, 35.300537611109924 ], [ -80.848617517999969, 35.299965078109913 ], [ -80.848841317999984, 35.29882001110991 ], [ -80.849539984999979, 35.297926878109948 ], [ -80.85068638499996, 35.294766478109928 ], [ -80.851999585, 35.293690278109956 ], [ -80.852279117999956, 35.293232278109969 ], [ -80.852279317999944, 35.292178678109977 ], [ -80.852167717999976, 35.291789278110002 ], [ -80.85211211799999, 35.290873211109961 ], [ -80.851832984999987, 35.289819611110033 ], [ -80.851163117999988, 35.288422411110048 ], [ -80.851163317999976, 35.287506278110101 ], [ -80.851302984999961, 35.287254278110005 ], [ -80.851219317999949, 35.286910811110019 ] ] } }, +{ "type": "Feature", "id": "fp-63", "properties": { "length_km": 0.718, "slope_percent": 0.10601113, "main_id": 90, "toid": "fp-66", "realized_catchment": "cat-63" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.844180584999947, 35.286153478110094 ], [ -80.845995984999945, 35.286795211110018 ], [ -80.846750117999989, 35.286795411110042 ], [ -80.847476317999963, 35.28700167811008 ], [ -80.84783931799997, 35.287185011110026 ], [ -80.848425784999975, 35.287803478109993 ], [ -80.84881678499994, 35.287757811110012 ], [ -80.849459517999932, 35.287345611109998 ], [ -80.850493117999974, 35.287025078110084 ], [ -80.851219317999949, 35.286910811110019 ] ] } }, +{ "type": "Feature", "id": "fp-64", "properties": { "length_km": 2.016, "slope_percent": 0.00978563, "main_id": 127, "toid": "fp-58", "realized_catchment": "cat-64" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.883229184999948, 35.27710981111013 ], [ -80.883033717999979, 35.27667467811014 ], [ -80.882670584999971, 35.276331211110147 ], [ -80.881497517999946, 35.275689878110057 ], [ -80.880966784999941, 35.275209011110164 ], [ -80.880603717999975, 35.274453211110135 ], [ -80.879793717999988, 35.273743278110103 ], [ -80.878201717999957, 35.27296461111019 ], [ -80.877754917999937, 35.272392011110234 ], [ -80.877503517999969, 35.2714300781102 ], [ -80.877280117999987, 35.271178078110204 ], [ -80.875241384999981, 35.271017878110136 ], [ -80.874794517999987, 35.270559878110269 ], [ -80.874598984999963, 35.270307878110216 ], [ -80.874291917999983, 35.270078878110127 ], [ -80.873090917999946, 35.269781078110235 ], [ -80.872783717999937, 35.269552078110216 ], [ -80.872253184999977, 35.269437611110206 ], [ -80.87191811799994, 35.269094011110198 ], [ -80.870940584999957, 35.269139811110293 ], [ -80.870549584999935, 35.268819078110191 ], [ -80.869963317999975, 35.267857078110254 ], [ -80.869935317999989, 35.267605211110229 ], [ -80.869851517999962, 35.267559411110184 ], [ -80.869795717999978, 35.266963878110246 ], [ -80.869488517999969, 35.26609361111025 ], [ -80.869153384999947, 35.265520878110259 ], [ -80.868985917999964, 35.265383478110188 ] ] } }, +{ "type": "Feature", "id": "fp-65", "properties": { "length_km": 4.279, "slope_percent": 0.00949624, "main_id": 97, "toid": "fp-120", "realized_catchment": "cat-65" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.898280584999938, 35.268541811110154 ], [ -80.89802918499997, 35.26815247811021 ], [ -80.897693917999973, 35.267602878110182 ], [ -80.897023384999954, 35.26670967811021 ], [ -80.896436784999935, 35.266091478110269 ], [ -80.895933984999957, 35.265748011110247 ], [ -80.895235784999954, 35.265519078110259 ], [ -80.89305771799998, 35.265427811110257 ], [ -80.892024384999957, 35.265267678110249 ], [ -80.890962917999957, 35.263733411110309 ], [ -80.890711584999963, 35.263641678110268 ], [ -80.890125117999958, 35.26364181111029 ], [ -80.889789984999936, 35.263367011110297 ], [ -80.88967818499998, 35.263023478110185 ], [ -80.888979984999935, 35.262061611110347 ], [ -80.888728517999937, 35.261214211110314 ], [ -80.888169917999946, 35.260618878110265 ], [ -80.888197717999958, 35.260458478110401 ], [ -80.888085984999975, 35.260389811110294 ], [ -80.887694784999951, 35.258603411110357 ], [ -80.887610984999981, 35.258557611110341 ], [ -80.886717317999967, 35.256954411110328 ], [ -80.885991117999936, 35.256152878110328 ], [ -80.885516517999989, 35.256038411110431 ], [ -80.884455385, 35.256038478110348 ], [ -80.883282584999961, 35.256153211110451 ], [ -80.882808117999957, 35.256061611110361 ], [ -80.881998184999986, 35.255260011110316 ], [ -80.881942317999972, 35.254893611110333 ], [ -80.882612384999959, 35.254458411110384 ], [ -80.88275191799994, 35.254229278110373 ], [ -80.882779784999968, 35.253908678110399 ], [ -80.882193384999937, 35.252763611110353 ], [ -80.881411384999979, 35.25056487811036 ], [ -80.881158917999983, 35.250149411110364 ], [ -80.88073998499992, 35.249760011110375 ], [ -80.879539384999973, 35.249256211110414 ], [ -80.876691584999946, 35.24815687811045 ], [ -80.875239784999962, 35.247744678110408 ], [ -80.874793184999987, 35.247721811110438 ], [ -80.874290717999941, 35.247699011110491 ], [ -80.873592584999983, 35.247905078110506 ], [ -80.872782918, 35.248821211110446 ], [ -80.872419917999935, 35.249050211110479 ], [ -80.871694117999937, 35.249141811110498 ], [ -80.871135717999948, 35.249073078110435 ], [ -80.871023917999949, 35.248981411110528 ], [ -80.871163584999977, 35.248477478110352 ], [ -80.871107784999936, 35.248294278110485 ], [ -80.870661117999987, 35.247927878110382 ], [ -80.869795717999978, 35.247424011110439 ] ] } }, +{ "type": "Feature", "id": "fp-66", "properties": { "length_km": 3.448, "slope_percent": 0.00735062, "main_id": 44, "toid": "fp-98", "realized_catchment": "cat-66" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.851219317999949, 35.286910811110019 ], [ -80.851582984999951, 35.28500987811006 ], [ -80.851583184999981, 35.284254078109981 ], [ -80.850829784999974, 35.281459678110075 ], [ -80.85080211799999, 35.28068101111009 ], [ -80.850243984999963, 35.279146411110112 ], [ -80.850132517999953, 35.278550811110151 ], [ -80.850048717999982, 35.27848207811013 ], [ -80.849937184999987, 35.277634678110189 ], [ -80.849378917999957, 35.276581011110153 ], [ -80.84904391799995, 35.27623741111023 ], [ -80.848150317999952, 35.275802078110232 ], [ -80.847982784999928, 35.275641678110034 ], [ -80.847955117999959, 35.274977478110088 ], [ -80.848541717999979, 35.274382211110193 ], [ -80.848541917999967, 35.273992811110119 ], [ -80.848262784999974, 35.273603411110166 ], [ -80.847899717999951, 35.273420078110185 ], [ -80.847145584999964, 35.273580278110181 ], [ -80.846224117999952, 35.273282278110209 ], [ -80.84588911799996, 35.272961478110233 ], [ -80.845749517999934, 35.272572078110144 ], [ -80.845526184999983, 35.27234307811019 ], [ -80.845582384999943, 35.271220811110098 ], [ -80.845191517999965, 35.271060411110213 ], [ -80.844269984999983, 35.270991411110245 ], [ -80.844214317999956, 35.270625011110234 ], [ -80.844409784999982, 35.270487611110212 ], [ -80.844521717999953, 35.270235811110169 ], [ -80.844354184999986, 35.269960811110238 ], [ -80.844313717999967, 35.269810811110183 ], [ -80.844353317999946, 35.269580678110259 ], [ -80.843664384999954, 35.26881261111027 ], [ -80.843929517999982, 35.26787021111025 ], [ -80.844075584999985, 35.267739211110225 ], [ -80.844019784999958, 35.267510078110234 ], [ -80.843377917999987, 35.26636481111035 ], [ -80.841144917999941, 35.263844811110246 ], [ -80.840558917999957, 35.262974411110285 ], [ -80.840335984999953, 35.261783211110277 ], [ -80.839973317999977, 35.260912878110311 ] ] } }, +{ "type": "Feature", "id": "fp-67", "properties": { "length_km": 3.448, "slope_percent": 0.00556844, "main_id": 12, "toid": "fp-90", "realized_catchment": "cat-67" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.799048717999938, 35.278530211110194 ], [ -80.799495784999976, 35.278255678110163 ], [ -80.799607917999936, 35.277912211110113 ], [ -80.799524184999939, 35.277843478110128 ], [ -80.799357317999977, 35.277133411110078 ], [ -80.79916211799997, 35.27678967811017 ], [ -80.79907871799999, 35.276354478110157 ], [ -80.799162717999934, 35.276125478110117 ], [ -80.800476384999982, 35.274958211110139 ], [ -80.800979784999981, 35.274202678110164 ], [ -80.801091984999971, 35.273721811110242 ], [ -80.801343784999972, 35.273218078110133 ], [ -80.802405917999977, 35.272142278110167 ], [ -80.803384717999961, 35.270700011110137 ], [ -80.803860517999965, 35.269417611110185 ], [ -80.803916784999956, 35.268913811110252 ], [ -80.804112984999961, 35.268066411110254 ], [ -80.804617717999975, 35.265661811110284 ], [ -80.805065317999947, 35.264860478110265 ], [ -80.805679984999983, 35.264288211110298 ], [ -80.806322984999966, 35.263418278110287 ], [ -80.806910717999983, 35.261861211110286 ], [ -80.80741411799994, 35.261105678110262 ], [ -80.807805717999955, 35.2602584111103 ], [ -80.808476717999937, 35.259296811110389 ], [ -80.808980517999942, 35.257762478110429 ], [ -80.808980984999948, 35.257006678110344 ], [ -80.809233584999959, 35.255426611110408 ], [ -80.809346517999984, 35.253869211110327 ], [ -80.809598584999947, 35.252907411110407 ], [ -80.809627384999942, 35.251853878110424 ], [ -80.810353517999943, 35.250132878110342 ] ] } }, +{ "type": "Feature", "id": "fp-68", "properties": { "length_km": 4.0520685907815022, "slope_percent": 0.00347077, "main_id": 23, "toid": "fp-69", "realized_catchment": "cat-68" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.957125917999988, 35.161576011111435 ], [ -80.957600117999974, 35.16166721111145 ], [ -80.957962584999962, 35.161575411111414 ], [ -80.95804611799997, 35.161460811111418 ], [ -80.958212317999951, 35.160361278111417 ], [ -80.958239117999938, 35.159193211111393 ], [ -80.958127117999936, 35.158849678111459 ], [ -80.958152584999922, 35.156422011111424 ], [ -80.958652917999984, 35.15474961111147 ], [ -80.959376917999975, 35.153672678111526 ], [ -80.959543784999923, 35.153237411111512 ], [ -80.959989517999972, 35.15271027811157 ], [ -80.960267785, 35.152229011111508 ], [ -80.960406717999945, 35.151702211111527 ], [ -80.961046984999939, 35.150648211111523 ], [ -80.961408184999982, 35.149388211111571 ], [ -80.96143511799994, 35.148449078111554 ], [ -80.961712317999968, 35.146891478111485 ], [ -80.961736584999969, 35.143387078111637 ], [ -80.962094517999958, 35.139035211111626 ], [ -80.961898584999972, 35.138371211111661 ], [ -80.960641784999964, 35.136356478111566 ], [ -80.959106517999942, 35.134525211111672 ], [ -80.956315984999947, 35.132030678111668 ], [ -80.954084117999969, 35.130497611111757 ], [ -80.953553784999954, 35.129833678111773 ], [ -80.95349758499998, 35.129375678111771 ], [ -80.953413917999981, 35.12932987811169 ], [ -80.953392604982255, 35.129176924072716 ] ] } }, +{ "type": "Feature", "id": "fp-69", "properties": { "length_km": 4.0520685907814178, "slope_percent": 0.00347077, "main_id": 23, "toid": "fp-72", "realized_catchment": "cat-69" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.953392604982255, 35.129176924072716 ], [ -80.953273517999932, 35.12832227811176 ], [ -80.953188784999952, 35.127062678111756 ], [ -80.952880317999984, 35.125161811111795 ], [ -80.953323184999988, 35.121725878111882 ], [ -80.953545317999968, 35.120924078111827 ], [ -80.953544717999989, 35.120259878111852 ], [ -80.953014517999989, 35.119573078111884 ], [ -80.952762584999974, 35.118565611111862 ], [ -80.952676384999961, 35.115771411111872 ], [ -80.95256438499996, 35.115290478111902 ], [ -80.952480784999977, 35.115244678111914 ], [ -80.952088984999989, 35.113550078111935 ], [ -80.951920317999964, 35.112038611112027 ], [ -80.952224985, 35.110022811112046 ], [ -80.952085184999987, 35.10954187811199 ], [ -80.951526917999956, 35.108557411111981 ], [ -80.951526384999966, 35.107984811112026 ], [ -80.952277517999946, 35.106472611112032 ], [ -80.952611717999957, 35.106197611112037 ], [ -80.953113117999976, 35.105968278111931 ], [ -80.953307984999981, 35.105739078111988 ], [ -80.953725117999966, 35.104685211112063 ], [ -80.954085917999976, 35.103035878112095 ], [ -80.954613784999935, 35.101294878112085 ], [ -80.955947984999966, 35.097743811112089 ], [ -80.956337517999941, 35.097010611112125 ], [ -80.957198917999975, 35.094582211112076 ], [ -80.95742171799999, 35.094421678112148 ] ] } }, +{ "type": "Feature", "id": "fp-70", "properties": { "length_km": 2.9547191004172597, "slope_percent": 0.00610416, "main_id": 103, "toid": "fp-71", "realized_catchment": "cat-70" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.997888384999953, 35.077919211112338 ], [ -80.996076384999981, 35.077233878112352 ], [ -80.995017784999959, 35.077326478112347 ], [ -80.994432717999985, 35.077281278112338 ], [ -80.993012117999967, 35.077488811112353 ], [ -80.991786317999981, 35.077581611112343 ], [ -80.990755784999976, 35.077857478112335 ], [ -80.990254184999969, 35.0779038111123 ], [ -80.989919517999965, 35.077606411112363 ], [ -80.98989151799999, 35.077469078112337 ], [ -80.989417584999956, 35.077332011112375 ], [ -80.988944117999949, 35.077401211112395 ], [ -80.988526384999943, 35.077699278112334 ], [ -80.988471384999968, 35.078203278112291 ], [ -80.98836018499999, 35.078317878112351 ], [ -80.98688391799999, 35.078594078112268 ], [ -80.985630584999967, 35.078938811112366 ], [ -80.984990118, 35.079191411112319 ], [ -80.983569717999956, 35.079604878112335 ], [ -80.983124517999954, 35.080063411112263 ], [ -80.982539517999953, 35.080224278112304 ], [ -80.979948517999958, 35.080386878112328 ], [ -80.979447317999984, 35.080547678112325 ], [ -80.979057784999952, 35.08098321111234 ], [ -80.978751384999953, 35.081143811112376 ], [ -80.976661917999948, 35.081328811112314 ], [ -80.975993184999936, 35.081329278112293 ], [ -80.975296384999979, 35.081077878112374 ], [ -80.974543984999968, 35.081009878112312 ], [ -80.973792317999937, 35.081514278112252 ], [ -80.973318584999959, 35.081537678112348 ], [ -80.973067717999982, 35.081377678112283 ], [ -80.972620517999985, 35.080141211112313 ], [ -80.971811917999943, 35.079637878112365 ], [ -80.97133738499997, 35.078997011112286 ], [ -80.970640517999982, 35.078654011112341 ], [ -80.970389384999976, 35.07837927811233 ], [ -80.970325224584784, 35.078116517302455 ] ] } }, +{ "type": "Feature", "id": "fp-71", "properties": { "length_km": 3.6097191004170615, "slope_percent": 0.00610416, "main_id": 103, "toid": "fp-4", "realized_catchment": "cat-71" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.970325224584784, 35.078116517302455 ], [ -80.970305517999975, 35.078035811112336 ], [ -80.970193784999935, 35.077944278112327 ], [ -80.969525117999979, 35.077830278112359 ], [ -80.968967584999973, 35.077624611112327 ], [ -80.968577117999985, 35.077304278112337 ], [ -80.968381784999963, 35.077006611112346 ], [ -80.968324584999948, 35.075655411112287 ], [ -80.968100984999978, 35.075128678112321 ], [ -80.96573058499996, 35.072977611112314 ], [ -80.965423984999973, 35.072794678112359 ], [ -80.96391938499994, 35.072681211112489 ], [ -80.963222984999959, 35.072819211112453 ], [ -80.96274918499995, 35.072659078112373 ], [ -80.962330984999937, 35.072315878112391 ], [ -80.960796584999969, 35.070209811112363 ], [ -80.960154517999968, 35.068973478112412 ], [ -80.95694851799999, 35.06670821111247 ], [ -80.953325317999941, 35.06496987811245 ], [ -80.951736517999962, 35.063871611112567 ], [ -80.951234917999969, 35.063688611112504 ], [ -80.95067791799994, 35.063826411112487 ], [ -80.948840117999978, 35.06476667811252 ], [ -80.948366717999988, 35.064812811112489 ], [ -80.948199384999953, 35.064698278112417 ], [ -80.948310517999971, 35.064331811112439 ], [ -80.948310117999938, 35.063896678112485 ], [ -80.948170717999972, 35.063736411112458 ] ] } }, +{ "type": "Feature", "id": "fp-72", "properties": { "length_km": 2.751108856064131, "slope_percent": 0.00107233, "main_id": 23, "toid": "fp-5", "realized_catchment": "cat-72" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.95742171799999, 35.094421678112148 ], [ -80.957449184999973, 35.094101011112144 ], [ -80.957838584999934, 35.093367878112154 ], [ -80.958060785, 35.092520211112181 ], [ -80.958144384999983, 35.092474411112207 ], [ -80.95808791799999, 35.091764411112187 ], [ -80.95772491799994, 35.0909858781122 ], [ -80.957473784999934, 35.090665411112305 ], [ -80.95738991799999, 35.090390678112243 ], [ -80.957194584999954, 35.090184611112136 ], [ -80.956720784999959, 35.090024611112121 ], [ -80.956357918, 35.089498011112191 ], [ -80.955856317999974, 35.08938401111218 ], [ -80.955382717999953, 35.089521811112171 ], [ -80.954881584999953, 35.090048811112219 ], [ -80.954463717999943, 35.090094878112254 ], [ -80.954212384999948, 35.089614078112184 ], [ -80.95379431799995, 35.089385411112275 ], [ -80.953096917999986, 35.088698678112259 ], [ -80.951870517999964, 35.088241411112215 ], [ -80.951508184999966, 35.088218678112227 ], [ -80.951257517999977, 35.088310478112213 ], [ -80.950310984999987, 35.08922721111216 ], [ -80.949725784999941, 35.089319211112226 ], [ -80.949475317999926, 35.089525478112236 ], [ -80.949308384999938, 35.090006611112166 ], [ -80.948779117999948, 35.090098478112189 ], [ -80.948304984999936, 35.089594878112194 ], [ -80.948304717999974, 35.089342878112227 ], [ -80.94855531799999, 35.089113678112199 ], [ -80.948554984999987, 35.088816011112293 ], [ -80.948359384999961, 35.088289411112136 ], [ -80.948275917999979, 35.088243611112247 ], [ -80.948052317999952, 35.087556611112213 ], [ -80.94799631799998, 35.087167411112226 ], [ -80.94780078499997, 35.086709411112253 ], [ -80.947242984999946, 35.086160011112177 ], [ -80.947242584999969, 35.085633211112238 ], [ -80.94738158499996, 35.085381211112292 ], [ -80.94816158499998, 35.085174611112237 ], [ -80.948718917999955, 35.085174211112225 ], [ -80.949610784999948, 35.08547127811223 ], [ -80.950168184999953, 35.085493878112267 ], [ -80.950307317999943, 35.085333478112275 ], [ -80.950892184999987, 35.085104078112238 ], [ -80.950891984999956, 35.084760611112259 ], [ -80.950473384999952, 35.084073678112247 ], [ -80.950166584999977, 35.083776078112265 ], [ -80.949469984999951, 35.083685011112244 ], [ -80.94910704541212, 35.083250370852269 ] ] } }, +{ "type": "Feature", "id": "fp-73", "properties": { "length_km": 3.1586349560175209, "slope_percent": 0.00403437, "main_id": 109, "toid": "fp-74", "realized_catchment": "cat-73" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.890747117999965, 35.15880907811141 ], [ -80.890942184999972, 35.158396811111395 ], [ -80.890942184999972, 35.158053211111394 ], [ -80.890663184999937, 35.15718287811147 ], [ -80.890328384999961, 35.156564611111484 ], [ -80.890133117999937, 35.155900478111519 ], [ -80.890160517999959, 35.15361007811147 ], [ -80.890299784999982, 35.152671011111536 ], [ -80.890522784999973, 35.152167011111544 ], [ -80.890439117999961, 35.151640211111527 ], [ -80.889629984999942, 35.150105878111553 ], [ -80.889295317999938, 35.149510411111585 ], [ -80.889155784999957, 35.149052411111612 ], [ -80.889183584999955, 35.148227811111553 ], [ -80.888988317999974, 35.147403278111568 ], [ -80.888402384999949, 35.145548078111595 ], [ -80.888485917999958, 35.144632011111575 ], [ -80.888653117999979, 35.143967811111573 ], [ -80.888764517999959, 35.142960011111619 ], [ -80.888206584999963, 35.141814878111653 ], [ -80.887983584999958, 35.141585811111611 ], [ -80.887871917999973, 35.141104811111617 ], [ -80.887230517999967, 35.14050947811166 ], [ -80.887202584999955, 35.140165878111624 ], [ -80.887397784999962, 35.139959811111567 ], [ -80.887453384999958, 35.139432878111684 ], [ -80.886951384999975, 35.138242011111586 ], [ -80.886756317999982, 35.138173278111665 ], [ -80.886170584999945, 35.136959478111699 ], [ -80.885054984999968, 35.135127211111659 ], [ -80.885082917999981, 35.134554611111618 ], [ -80.884915517999971, 35.134256878111735 ], [ -80.884497384999975, 35.133982078111742 ], [ -80.88410698499996, 35.134050811111713 ], [ -80.883856118, 35.133867611111718 ], [ -80.883800184999984, 35.133386678111705 ], [ -80.883465584999982, 35.132882811111742 ], [ -80.883449669452091, 35.13267347529878 ] ] } }, +{ "type": "Feature", "id": "fp-74", "properties": { "length_km": 3.1586349560173752, "slope_percent": 0.00403437, "main_id": 109, "toid": "fp-34", "realized_catchment": "cat-74" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.883449669452091, 35.13267347529878 ], [ -80.883437717999982, 35.132516278111694 ], [ -80.883939584999951, 35.132241411111728 ], [ -80.884051117999945, 35.132104011111757 ], [ -80.884134717999984, 35.131416878111715 ], [ -80.884608717999967, 35.130958678111725 ], [ -80.884608584999967, 35.130546478111739 ], [ -80.884022984999945, 35.129974011111734 ], [ -80.884022984999945, 35.129767811111748 ], [ -80.884273917999963, 35.12924101111178 ], [ -80.884887184999968, 35.128828678111788 ], [ -80.885137984999972, 35.128393478111789 ], [ -80.885862784999972, 35.127797878111799 ], [ -80.885500184999955, 35.127202478111769 ], [ -80.885416584999959, 35.126858878111818 ], [ -80.885723184999961, 35.126538278111781 ], [ -80.886197117999984, 35.125805278111784 ], [ -80.887590917999944, 35.125140878111829 ], [ -80.888510784999937, 35.124614078111762 ], [ -80.889319117999946, 35.124453678111834 ], [ -80.889542117999952, 35.124362011111813 ], [ -80.889932184999964, 35.122850278111756 ], [ -80.89043371799994, 35.121453078111863 ], [ -80.890405717999954, 35.121063678111774 ], [ -80.889848117999975, 35.120468411111851 ], [ -80.889764384999978, 35.120033211111888 ], [ -80.890126584999962, 35.118956611111869 ], [ -80.890851317999989, 35.118452611111877 ], [ -80.891157784999962, 35.11801741111185 ], [ -80.892133384999966, 35.117490411111881 ], [ -80.892356317999941, 35.117146811111887 ], [ -80.892634717999954, 35.115703878111937 ], [ -80.89269038499998, 35.114444078111923 ], [ -80.892606717999954, 35.114238011111972 ], [ -80.892606384999965, 35.113276011111871 ], [ -80.892940917999965, 35.112795011111999 ], [ -80.89285698499998, 35.112107878111971 ], [ -80.892996384999961, 35.11178727811194 ], [ -80.893553784999938, 35.111443611111987 ], [ -80.893692984999973, 35.11103127811198 ], [ -80.894278317999976, 35.110802211111974 ], [ -80.894501317999982, 35.110550211111985 ], [ -80.89528158499995, 35.110321011112013 ], [ -80.895532384999967, 35.110092011111973 ], [ -80.89567171799996, 35.109794211112018 ] ] } }, +{ "type": "Feature", "id": "fp-75", "properties": { "length_km": 3.1901383961504806, "slope_percent": 0.00648097, "main_id": 98, "toid": "fp-76", "realized_catchment": "cat-75" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.846924117999947, 35.026582611112836 ], [ -80.847257784999954, 35.027727878112927 ], [ -80.847341317999962, 35.027773678112894 ], [ -80.847368984999946, 35.028002811112863 ], [ -80.848148317999957, 35.029194011112864 ], [ -80.849651517999973, 35.030499878112821 ], [ -80.849874184999976, 35.030866278112846 ], [ -80.850096717999975, 35.031530611112807 ], [ -80.850291584999979, 35.031828478112963 ], [ -80.850736984999969, 35.032195011112798 ], [ -80.85157238499994, 35.032470011112771 ], [ -80.85196211799996, 35.032767878112836 ], [ -80.852630184999953, 35.033111478112879 ], [ -80.853298517999974, 35.033569678112791 ], [ -80.85488558499999, 35.034165478112833 ], [ -80.855414784999923, 35.033318078112835 ], [ -80.856528917999981, 35.032837278112815 ], [ -80.858227584999952, 35.032264878112763 ], [ -80.859258117999985, 35.03132587811286 ], [ -80.859926384999952, 35.03093667811283 ], [ -80.861764317999985, 35.030295611112862 ], [ -80.863100917999986, 35.030227011112906 ], [ -80.863574317999962, 35.03036447811283 ], [ -80.86401991799994, 35.030295811112843 ], [ -80.864549117999957, 35.029654478112924 ], [ -80.864771784999959, 35.029562878112813 ], [ -80.86630331799995, 35.029333878112809 ], [ -80.866915984999935, 35.029082011112799 ], [ -80.867612117999954, 35.028967478112868 ], [ -80.868892984999945, 35.028967611112911 ], [ -80.86933851799995, 35.028876011112885 ], [ -80.869505584999956, 35.028669811112835 ], [ -80.869867717999966, 35.027089478112877 ], [ -80.871037184999977, 35.025211411112913 ], [ -80.871498843071763, 35.024988027901756 ] ] } }, +{ "type": "Feature", "id": "fp-76", "properties": { "length_km": 3.4931383961505675, "slope_percent": 0.00648097, "main_id": 98, "toid": "fp-129", "realized_catchment": "cat-76" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.871498843071763, 35.024988027901756 ], [ -80.871983984999986, 35.024753278112883 ], [ -80.872791384999971, 35.02392881111296 ], [ -80.874628984999958, 35.023860078112854 ], [ -80.874796117999949, 35.023928811113009 ], [ -80.87529731799998, 35.023997411112838 ], [ -80.876466717999975, 35.023195811112892 ], [ -80.877190584999937, 35.02276067811291 ], [ -80.878025984999965, 35.022691878112852 ], [ -80.878499317999982, 35.022348278113007 ], [ -80.879000517999941, 35.022165078112934 ], [ -80.879445917999988, 35.021340478112904 ], [ -80.879640717999962, 35.020767878112984 ], [ -80.880058317999953, 35.019966211112909 ], [ -80.880531717999986, 35.019874478112953 ], [ -80.88092151799998, 35.019989078112978 ], [ -80.881394784999941, 35.020012011112961 ], [ -80.882090784999946, 35.019920278112977 ], [ -80.882786917999951, 35.020149211113008 ], [ -80.882898184999988, 35.01996587811302 ], [ -80.883148584999958, 35.018362611113005 ], [ -80.883343517999947, 35.017858678113015 ], [ -80.884345584999963, 35.017240278113071 ], [ -80.884930317999988, 35.016484411113012 ], [ -80.885375717999977, 35.016232411113016 ], [ -80.885709717999987, 35.015843011113056 ], [ -80.885988117999929, 35.015155811112969 ], [ -80.886015717999953, 35.014491611113009 ], [ -80.886210517999984, 35.013919011113046 ], [ -80.88657251799998, 35.013804411113043 ], [ -80.887519117999943, 35.013781478113032 ], [ -80.888799784999946, 35.013575211113022 ], [ -80.889328717999945, 35.013758278113102 ], [ -80.890303184999937, 35.013712411113069 ], [ -80.891054784999938, 35.013826811113034 ], [ -80.891722784999956, 35.01307087811302 ], [ -80.892279517999953, 35.012727211113116 ], [ -80.892919784999947, 35.012589678113081 ], [ -80.894478785, 35.012589411113019 ], [ -80.895592385, 35.012818211113128 ], [ -80.896177117999969, 35.013093078113059 ], [ -80.896622717999946, 35.013459411113061 ], [ -80.89690111799996, 35.013550878112994 ] ] } }, +{ "type": "Feature", "id": "fp-77", "properties": { "length_km": 3.3725637762935121, "slope_percent": 0.00078131, "main_id": 8, "toid": "fp-78", "realized_catchment": "cat-77" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.869224184999965, 35.066507478112477 ], [ -80.870505584999989, 35.066530411112474 ], [ -80.870756384999936, 35.066416011112494 ], [ -80.871174317999987, 35.066049478112511 ], [ -80.871480717999987, 35.065934878112472 ], [ -80.872121384999957, 35.066003678112494 ], [ -80.872177184999941, 35.06575167811242 ], [ -80.871285717999967, 35.064583678112541 ], [ -80.869865184999981, 35.063094811112485 ], [ -80.86975371799997, 35.06263667811254 ], [ -80.870143784999982, 35.062018411112483 ], [ -80.870450184999967, 35.061170878112478 ], [ -80.870728784999983, 35.060941878112516 ], [ -80.871285917999955, 35.060758611112476 ], [ -80.873625784999945, 35.060758678112514 ], [ -80.874405784999965, 35.060667078112509 ], [ -80.874795717999987, 35.060896011112526 ], [ -80.875213584999983, 35.061743478112561 ], [ -80.875770717999941, 35.062476411112499 ], [ -80.876272184999962, 35.062934478112446 ], [ -80.877971384999967, 35.063232211112485 ], [ -80.87983778499995, 35.063323811112518 ], [ -80.880116384999951, 35.063232211112485 ], [ -80.880617717999925, 35.062567878112453 ], [ -80.880840517999957, 35.062018211112495 ], [ -80.881592584999964, 35.060827211112503 ], [ -80.882121917999939, 35.060277478112525 ], [ -80.881982384999958, 35.058651211112512 ], [ -80.88206598499994, 35.058422211112543 ], [ -80.882344584999956, 35.058261811112601 ], [ -80.883625784999921, 35.058215878112541 ], [ -80.884071518, 35.057895211112573 ], [ -80.884349984999972, 35.057414211112594 ], [ -80.884322117999957, 35.056589678112587 ], [ -80.88412698499998, 35.056269011112555 ], [ -80.884238384999946, 35.055627678112565 ], [ -80.884600384999942, 35.055215278112627 ], [ -80.884600384999942, 35.054642678112536 ], [ -80.884238184999944, 35.054161878112531 ], [ -80.884238117999985, 35.053841211112626 ], [ -80.884628117999966, 35.053657878112624 ], [ -80.885268717999963, 35.053520411112579 ], [ -80.885835436829666, 35.053658896658092 ] ] } }, +{ "type": "Feature", "id": "fp-78", "properties": { "length_km": 3.3725637762937413, "slope_percent": 0.00078131, "main_id": 8, "toid": "fp-23", "realized_catchment": "cat-78" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.885835436829666, 35.053658896658092 ], [ -80.887329917999978, 35.054024078112533 ], [ -80.888026317999959, 35.053932411112612 ], [ -80.888499784999965, 35.053703278112586 ], [ -80.88863891799997, 35.053245211112589 ], [ -80.889223717999982, 35.052603878112606 ], [ -80.889223584999968, 35.052100011112522 ], [ -80.888805784999988, 35.051939678112639 ], [ -80.888583117999985, 35.052054211112527 ], [ -80.888415984999938, 35.052352011112589 ], [ -80.888193117999947, 35.052535211112556 ], [ -80.887580384999978, 35.052718478112702 ], [ -80.88671698499995, 35.052764411112591 ], [ -80.886020584999969, 35.052169011112554 ], [ -80.885519117999948, 35.051596478112565 ], [ -80.885546917999989, 35.050634478112585 ], [ -80.885407584999939, 35.050405411112607 ], [ -80.88504551799997, 35.050107678112695 ], [ -80.884711184999972, 35.049626811112589 ], [ -80.884655384999959, 35.048985478112719 ], [ -80.884850384999936, 35.048687678112671 ], [ -80.885268117999942, 35.048412811112641 ], [ -80.886270717999935, 35.048137811112632 ], [ -80.886939118, 35.047840078112564 ], [ -80.887551784999971, 35.047679678112623 ], [ -80.887691117999964, 35.047450611112616 ], [ -80.887941584999965, 35.046259478112709 ], [ -80.888108717999955, 35.045938878112686 ], [ -80.889695984999946, 35.044930878112702 ], [ -80.890057917999968, 35.044449878112722 ], [ -80.890698384999951, 35.044243678112757 ], [ -80.891171784999983, 35.043327411112671 ], [ -80.891700718, 35.042663011112751 ], [ -80.891561384999989, 35.042182211112724 ], [ -80.891561317999958, 35.041632478112739 ], [ -80.891700517999936, 35.041403278112753 ], [ -80.892368784999974, 35.040876478112764 ], [ -80.892619317999959, 35.040532878112764 ], [ -80.893120517999989, 35.040028878112722 ], [ -80.894234117999986, 35.038746078112801 ], [ -80.894512584999958, 35.038654411112766 ], [ -80.894902317999936, 35.038036011112766 ], [ -80.894957784999974, 35.037096878112806 ], [ -80.895848784999941, 35.036547011112766 ], [ -80.896154984999953, 35.03558501111285 ], [ -80.89651678499996, 35.035012278112887 ], [ -80.896962317999964, 35.034737411112808 ], [ -80.897435584999982, 35.034164678112738 ] ] } }, +{ "type": "Feature", "id": "fp-79", "properties": { "length_km": 3.7180571469417423, "slope_percent": 0.00390532, "main_id": 24, "toid": "fp-80", "realized_catchment": "cat-79" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.733015096420075, 35.100378363988362 ], [ -80.734008384999981, 35.10061061111201 ], [ -80.734983717999967, 35.100541078112165 ], [ -80.735707717999972, 35.100173878112052 ], [ -80.73637658499996, 35.100173278112081 ], [ -80.736433984999962, 35.101410078112075 ], [ -80.73743851799999, 35.102302411112042 ], [ -80.73749458499999, 35.102554278112102 ], [ -80.737633984999945, 35.102645811112076 ], [ -80.737884717999975, 35.102554011112076 ], [ -80.738663784999972, 35.101591278112011 ], [ -80.73972158499997, 35.100742878112072 ], [ -80.739692917999974, 35.100216078112133 ], [ -80.74002571799997, 35.099070478112019 ], [ -80.740387917999954, 35.098932678112071 ], [ -80.741279584999987, 35.098886078112095 ], [ -80.741697717999955, 35.098977278112081 ], [ -80.742534584999987, 35.09952621111205 ], [ -80.742896984999959, 35.099617411112114 ], [ -80.743705117999966, 35.09952507811208 ], [ -80.743956117999986, 35.099639278112079 ], [ -80.744402584999989, 35.100074078112023 ], [ -80.744681317999948, 35.100073811112075 ], [ -80.744820384999969, 35.099936211112087 ], [ -80.745042584999965, 35.099363411112101 ], [ -80.745265184999937, 35.099134078112101 ], [ -80.747966984999948, 35.098169478112055 ], [ -80.748775184999943, 35.098145811112026 ], [ -80.749584517999949, 35.098992411112043 ], [ -80.749779784999987, 35.099106811112094 ], [ -80.751754717999972, 35.099198678112089 ], [ -80.753036984999937, 35.098948078112059 ], [ -80.754515184999946, 35.098125078112055 ], [ -80.754989718, 35.097667411112134 ], [ -80.755548984999962, 35.096316611112186 ], [ -80.757807317999962, 35.095608811112143 ], [ -80.758114317999983, 35.095357211112159 ], [ -80.759285317999982, 35.094877211112042 ], [ -80.761097317999941, 35.094512611112116 ], [ -80.762601917999987, 35.094697211112113 ], [ -80.763576717999968, 35.095087278112175 ], [ -80.763827517999971, 35.095110478112154 ], [ -80.764151761071858, 35.094991337633367 ] ] } }, +{ "type": "Feature", "id": "fp-80", "properties": { "length_km": 3.7180571469417338, "slope_percent": 0.00390532, "main_id": 24, "toid": "fp-26", "realized_catchment": "cat-80" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.764151761071858, 35.094991337633367 ], [ -80.764635984999984, 35.094813411112192 ], [ -80.76519351799999, 35.094768211112161 ], [ -80.765583517999971, 35.094906011112144 ], [ -80.766029317999937, 35.094929211112124 ], [ -80.766531184999963, 35.094654878112181 ], [ -80.766865584999948, 35.094655211112126 ], [ -80.767171917999974, 35.094815678112141 ], [ -80.767645717999983, 35.094839078112074 ], [ -80.767840984999964, 35.094656011112122 ], [ -80.768398584999943, 35.094473278112133 ], [ -80.768872917999943, 35.09399267811213 ], [ -80.769430584999952, 35.093718411112199 ], [ -80.769904384999947, 35.093650011112231 ], [ -80.770433717999936, 35.093856611112109 ], [ -80.771185917999958, 35.09394887811213 ], [ -80.772050517999958, 35.093399878112152 ], [ -80.772274118, 35.092873278112222 ], [ -80.772582517999979, 35.091453478112179 ], [ -80.773559717999944, 35.089896878112178 ], [ -80.774647984999959, 35.088638011112216 ], [ -80.77537298499999, 35.088203478112213 ], [ -80.776961917999984, 35.087769611112144 ], [ -80.778522517999974, 35.087587478112198 ], [ -80.77877318499999, 35.087656411112164 ], [ -80.779079384999989, 35.087885811112294 ], [ -80.780695517999959, 35.088001478112218 ], [ -80.780974317999949, 35.087864278112185 ], [ -80.781726584999944, 35.087819078112233 ], [ -80.782395717999975, 35.087544811112195 ], [ -80.783036717999948, 35.087499411112248 ], [ -80.783510584999988, 35.08733947811222 ], [ -80.783984184999952, 35.087408611112231 ], [ -80.784290784999925, 35.087340011112232 ], [ -80.784402517999965, 35.08713401111217 ], [ -80.784430784999984, 35.086721811112184 ], [ -80.784737584999959, 35.086424211112273 ], [ -80.785406384999987, 35.086356011112251 ], [ -80.785713118000018, 35.086173011112272 ], [ -80.786410584999928, 35.085394678112245 ], [ -80.786410984999975, 35.085051211112258 ], [ -80.786132917999979, 35.084547011112257 ], [ -80.786133184999983, 35.084157678112298 ], [ -80.786245117999954, 35.083837078112204 ], [ -80.786719184999981, 35.083425211112285 ], [ -80.786858984999981, 35.0830130111122 ], [ -80.787082117999944, 35.082784078112326 ], [ -80.787779184999977, 35.082372411112267 ], [ -80.788810917999967, 35.081548611112346 ], [ -80.789424717999964, 35.080793211112301 ], [ -80.790540384999986, 35.079648811112342 ], [ -80.791153584999961, 35.079397278112317 ], [ -80.792741784999976, 35.07935261111232 ], [ -80.793466317999957, 35.079238411112314 ] ] } }, +{ "type": "Feature", "id": "fp-81", "properties": { "length_km": 2.9377348555634599, "slope_percent": 0.00064, "main_id": 8, "toid": "fp-82", "realized_catchment": "cat-81" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.792310384999951, 35.119960811111866 ], [ -80.793899784999951, 35.119549678111845 ], [ -80.794819717999985, 35.119504411111848 ], [ -80.795655784999951, 35.119757011111801 ], [ -80.797633917999988, 35.120880478111921 ], [ -80.798135517999981, 35.121041078111816 ], [ -80.79860931799999, 35.121041411111868 ], [ -80.801399317999952, 35.118432078111894 ], [ -80.801817584999981, 35.118272011111877 ], [ -80.802403117999972, 35.118203678111875 ], [ -80.802709584999945, 35.11829541111188 ], [ -80.803405984999969, 35.118868411111826 ], [ -80.804771717999969, 35.11902961111187 ], [ -80.805830984999943, 35.119030211111919 ], [ -80.806612117999975, 35.11838927811192 ], [ -80.80809071799996, 35.116901278111854 ], [ -80.808621117999976, 35.116100011111904 ], [ -80.808900317999985, 35.115527611111922 ], [ -80.808900784999921, 35.114886278111982 ], [ -80.808789517999983, 35.114611278111859 ], [ -80.808483317999958, 35.114176011111972 ], [ -80.807591917999972, 35.113396678111997 ], [ -80.807425117999983, 35.113007278111944 ], [ -80.808150317999946, 35.112297678111929 ], [ -80.809405117999972, 35.111702878111963 ], [ -80.810715385, 35.111405811111943 ], [ -80.811718784999982, 35.111314678111938 ], [ -80.812304117999986, 35.111475411111925 ], [ -80.812833584999964, 35.111521411111958 ], [ -80.813028917999986, 35.111406878112021 ], [ -80.81325211799998, 35.111017678111978 ], [ -80.813196717999986, 35.110605411111912 ], [ -80.813144172764112, 35.110494882070356 ] ] } }, +{ "type": "Feature", "id": "fp-82", "properties": { "length_km": 2.9377348555636105, "slope_percent": 0.00064, "main_id": 8, "toid": "fp-27", "realized_catchment": "cat-82" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.813144172764112, 35.110494882070356 ], [ -80.812946184999987, 35.110078411111914 ], [ -80.811720784999977, 35.108772411111929 ], [ -80.811720917999935, 35.108635011112021 ], [ -80.812111717999969, 35.107902278111965 ], [ -80.812223517999939, 35.107467211112031 ], [ -80.812390984999965, 35.107123611112094 ], [ -80.813590317999967, 35.106047678112027 ], [ -80.814621917999943, 35.105704678112062 ], [ -80.815736984999944, 35.105178478112052 ], [ -80.817214517999957, 35.104744011112025 ], [ -80.817493384999977, 35.10449207811206 ], [ -80.817939784999965, 35.103736478112026 ], [ -80.818358384999954, 35.102935211112026 ], [ -80.818136584999934, 35.101217211112072 ], [ -80.81841598499993, 35.100209611112049 ], [ -80.819950117999966, 35.098446611112045 ], [ -80.821093584999971, 35.097004211112115 ], [ -80.821039384999935, 35.09475961111216 ], [ -80.821123317999934, 35.094255678112091 ], [ -80.821290784999974, 35.093912211112148 ], [ -80.821904117999964, 35.09350021111208 ], [ -80.824747384999966, 35.092104411112146 ], [ -80.826280184999973, 35.091692678112203 ], [ -80.827562184999977, 35.091464078112175 ], [ -80.827868784999964, 35.091372611112114 ] ] } }, +{ "type": "Feature", "id": "fp-83", "properties": { "length_km": 4.6948971139398319, "slope_percent": 0.00076783, "main_id": 12, "toid": "fp-21", "realized_catchment": "cat-83" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.862944917999982, 35.138288011111648 ], [ -80.862972917999969, 35.138104678111645 ], [ -80.863418984999967, 35.13762381111178 ], [ -80.863809517999982, 35.136891011111587 ], [ -80.864367184999935, 35.136272611111636 ], [ -80.864841384999977, 35.134783878111712 ], [ -80.86484171799998, 35.133203478111739 ], [ -80.865008984999974, 35.132447678111767 ], [ -80.86520431799994, 35.13208127811167 ], [ -80.86500931799999, 35.130203078111805 ], [ -80.864479717999984, 35.129493078111729 ], [ -80.864256917999967, 35.127637811111818 ], [ -80.86434058499999, 35.127477611111786 ], [ -80.86475891799995, 35.127088211111783 ], [ -80.865009717999939, 35.126767611111767 ], [ -80.865400184999942, 35.125759811111806 ], [ -80.865790584999957, 35.125141478111843 ], [ -80.866376117999948, 35.124431478111823 ], [ -80.867017317999967, 35.123171811111852 ], [ -80.867575117999948, 35.120973078111803 ], [ -80.867770384999972, 35.119965211111897 ], [ -80.868718317999935, 35.116896078111978 ], [ -80.868746384999938, 35.116048678111902 ], [ -80.869025317999956, 35.114720278111932 ], [ -80.869415517999983, 35.114285078111998 ], [ -80.869722184999944, 35.113781278111908 ], [ -80.870224117999953, 35.11181161111196 ], [ -80.870697984999936, 35.110643478111939 ], [ -80.871506384999989, 35.109498411111993 ], [ -80.872370384999954, 35.108994478112059 ], [ -80.873011384999984, 35.108330211111976 ], [ -80.87479531799994, 35.106062811112011 ], [ -80.875408384999957, 35.105123678112015 ], [ -80.875631384999977, 35.10450521111202 ], [ -80.876355984999989, 35.101504811112051 ], [ -80.876969117999963, 35.100382478112138 ], [ -80.877380137153864, 35.099382551446645 ] ] } }, +{ "type": "Feature", "id": "fp-84", "properties": { "length_km": 2.8886008180525637, "slope_percent": 0.00094858, "main_id": 1, "toid": "fp-85", "realized_catchment": "cat-84" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.920685318, 35.131981011111769 ], [ -80.920657317999954, 35.131843611111734 ], [ -80.92035051799995, 35.131545878111694 ], [ -80.920350384999949, 35.131362678111735 ], [ -80.920489717999942, 35.131202278111687 ], [ -80.921381717999964, 35.130881211111756 ], [ -80.921381517999976, 35.130537678111736 ], [ -80.921102584999957, 35.130194278111759 ], [ -80.921102317999981, 35.129873611111741 ], [ -80.921324984999956, 35.129300878111735 ], [ -80.921213517999945, 35.129186411111725 ], [ -80.920265517999965, 35.129003678111779 ], [ -80.919874984999979, 35.128728878111758 ], [ -80.919763384999953, 35.128500011111804 ], [ -80.91954031799996, 35.128339678111764 ], [ -80.918926984999985, 35.128408611111844 ], [ -80.91864798499995, 35.128248411111741 ], [ -80.917811717999939, 35.128317411111773 ], [ -80.917449317999967, 35.128088478111756 ], [ -80.916752317999979, 35.128088678111823 ], [ -80.916556784999955, 35.127218411111805 ], [ -80.916277717999947, 35.126737478111785 ], [ -80.91591518499996, 35.126485678111727 ], [ -80.914604784999938, 35.126119678111813 ], [ -80.913712917999987, 35.126715478111791 ], [ -80.912988117999987, 35.126715811111801 ], [ -80.912653384999942, 35.126441078111839 ], [ -80.91268118499994, 35.126051678111821 ], [ -80.911983984999949, 35.125639611111872 ], [ -80.911788917999957, 35.125616878111778 ], [ -80.911315117999948, 35.125891811111828 ], [ -80.910673917999972, 35.125937811111861 ], [ -80.909363317999976, 35.125159411111831 ], [ -80.908777717999939, 35.124747278111784 ], [ -80.908777517999965, 35.124083078111831 ], [ -80.909111584999948, 35.123235611111824 ], [ -80.909529717999987, 35.12302927811178 ], [ -80.910393917999954, 35.123052011111795 ], [ -80.910867717999963, 35.122845678111887 ], [ -80.91109058499994, 35.122502078111836 ], [ -80.910978984999986, 35.12238761111184 ], [ -80.910170517999973, 35.122067211111776 ], [ -80.909807984999986, 35.122044411111844 ], [ -80.909613117999982, 35.122388011111852 ], [ -80.909417984999948, 35.122548278111829 ], [ -80.909027585, 35.12238821111184 ], [ -80.908247117999963, 35.122411278111876 ], [ -80.907773117999952, 35.122113611111878 ], [ -80.907215517999987, 35.121976411111859 ], [ -80.906741584999963, 35.122136811111858 ], [ -80.906017117999966, 35.12280127811178 ], [ -80.905320184999923, 35.122916011111776 ], [ -80.905147883103695, 35.122845266536814 ] ] } }, +{ "type": "Feature", "id": "fp-85", "properties": { "length_km": 2.8886008180527893, "slope_percent": 0.00094858, "main_id": 1, "toid": "fp-34", "realized_catchment": "cat-85" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.905147883103695, 35.122845266536814 ], [ -80.904706917999988, 35.122664211111875 ], [ -80.904372317999957, 35.122618478111775 ], [ -80.903759117999982, 35.122756078111799 ], [ -80.903396717999954, 35.122664478111851 ], [ -80.903201384999988, 35.122481278111749 ], [ -80.903201384999988, 35.122160678111797 ], [ -80.903340584999967, 35.121885811111852 ], [ -80.903340517999936, 35.12161101111181 ], [ -80.902002517999946, 35.121496678111853 ], [ -80.900998717999983, 35.121130478111844 ], [ -80.900608384999941, 35.120832811111796 ], [ -80.900608317999968, 35.120443478111888 ], [ -80.901026317999921, 35.120237211111807 ], [ -80.90133291799998, 35.119870811111866 ], [ -80.901332784999965, 35.119664611111872 ], [ -80.901193317999983, 35.119389678111823 ], [ -80.900858717999924, 35.119092078111933 ], [ -80.900830717999966, 35.118725611111856 ], [ -80.900942184999977, 35.11849661111188 ], [ -80.901165117999923, 35.118313278111877 ], [ -80.90188978499998, 35.118244411111853 ], [ -80.902196517999982, 35.118313078111896 ], [ -80.902670317999949, 35.118175478111894 ], [ -80.902837584999986, 35.118038011111871 ], [ -80.903394584999944, 35.116526278111955 ], [ -80.904063517999987, 35.116411611111943 ], [ -80.904453584999956, 35.116205411111885 ], [ -80.904676584999962, 35.115884611111895 ], [ -80.904704384999945, 35.115586878111891 ], [ -80.904369717999927, 35.115358011111901 ], [ -80.903226917999973, 35.115358211111882 ], [ -80.902334917999966, 35.115243878111905 ], [ -80.901833117999956, 35.115037878111934 ], [ -80.901526317999981, 35.114786011111889 ], [ -80.901470717999985, 35.114442478111954 ], [ -80.901080184999955, 35.113961478111889 ], [ -80.900383117999979, 35.112953878111924 ], [ -80.900020584999936, 35.112633278111943 ], [ -80.899769584999945, 35.112266878111953 ], [ -80.899546317999977, 35.111259211111879 ], [ -80.899239584999975, 35.110617878111995 ], [ -80.899378784999953, 35.110022478111979 ], [ -80.899573784999973, 35.109862078111945 ], [ -80.899713184999939, 35.109541411111955 ], [ -80.899350584999979, 35.109083411111961 ], [ -80.898988117999977, 35.108739878111962 ], [ -80.898709517999976, 35.108694211111967 ], [ -80.898347184999977, 35.10908361111197 ], [ -80.898207984999942, 35.10965627811202 ], [ -80.897957317999953, 35.109839611112044 ], [ -80.896842384999957, 35.109977211111982 ], [ -80.896424184999944, 35.109702411111947 ], [ -80.89567171799996, 35.109794211112018 ] ] } }, +{ "type": "Feature", "id": "fp-86", "properties": { "length_km": 4.4561392594411728, "slope_percent": 0.00104465, "main_id": 1, "toid": "fp-87", "realized_catchment": "cat-86" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.915027184999985, 35.19366267811106 ], [ -80.915082785, 35.193273278110979 ], [ -80.913994317999936, 35.192655278111005 ], [ -80.913575717999947, 35.192311811111125 ], [ -80.913435984999978, 35.191922478111053 ], [ -80.913463517999958, 35.191029278111095 ], [ -80.914021317999982, 35.190456478111123 ], [ -80.914439584999968, 35.190296078111061 ], [ -80.914774584999975, 35.190456278111057 ], [ -80.915137517999938, 35.190960078111047 ], [ -80.915416584999946, 35.191211811111089 ], [ -80.915890917999945, 35.191372011111113 ], [ -80.916420984999945, 35.191097011111047 ], [ -80.916895117999957, 35.190661678111063 ], [ -80.917703984999946, 35.190340611111125 ], [ -80.918150184999945, 35.19004267811102 ], [ -80.918345384999952, 35.189790811111074 ], [ -80.918344984999962, 35.189126478111135 ], [ -80.918177317999948, 35.188554011111059 ], [ -80.918093584999951, 35.188508278111087 ], [ -80.918065517999935, 35.188027278111079 ], [ -80.918427984999923, 35.187660678111129 ], [ -80.918427584999961, 35.186996478111155 ], [ -80.918594784999982, 35.186584078111153 ], [ -80.919040917999951, 35.186103011111079 ], [ -80.919096384999989, 35.185530411111188 ], [ -80.918705384999953, 35.184728878111173 ], [ -80.91828671799999, 35.184202278111137 ], [ -80.918258184999956, 35.183102878111157 ], [ -80.918424584999983, 35.181041478111176 ], [ -80.917865784999947, 35.179553078111297 ], [ -80.917837584999972, 35.178866011111239 ], [ -80.918981117999977, 35.178292878111193 ], [ -80.919343384999934, 35.177720078111264 ], [ -80.919397784999944, 35.174948811111172 ], [ -80.91925738499998, 35.173483011111259 ], [ -80.91872718499998, 35.172864811111289 ], [ -80.918587517999981, 35.172498478111358 ], [ -80.918503184999963, 35.171353411111319 ], [ -80.918195917999981, 35.170620478111232 ], [ -80.917665584999952, 35.169819078111281 ], [ -80.916856517999975, 35.169292611111317 ], [ -80.914484917999971, 35.16752981111128 ], [ -80.913229184999977, 35.166453678111317 ], [ -80.912698984999963, 35.165881278111407 ], [ -80.912698585, 35.16507967811129 ], [ -80.912782184999969, 35.164896411111322 ], [ -80.912837584999963, 35.16402601111141 ], [ -80.912474117999977, 35.161919011111372 ], [ -80.912471800107227, 35.161694602332794 ] ] } }, +{ "type": "Feature", "id": "fp-87", "properties": { "length_km": 4.4561392594412634, "slope_percent": 0.00104465, "main_id": 1, "toid": "fp-84", "realized_catchment": "cat-87" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.912471800107227, 35.161694602332794 ], [ -80.912444784999948, 35.159079011111409 ], [ -80.912137585, 35.15797981111146 ], [ -80.911913784999967, 35.156353678111472 ], [ -80.912024717999941, 35.154842011111498 ], [ -80.912303384999973, 35.154406678111492 ], [ -80.913251384999967, 35.153925278111487 ], [ -80.913836784999944, 35.153444211111513 ], [ -80.914031917999978, 35.153146411111585 ], [ -80.914002917999937, 35.150833078111496 ], [ -80.913807517999942, 35.150329278111542 ], [ -80.913807384999984, 35.150169011111508 ], [ -80.91338871799995, 35.149344611111552 ], [ -80.912970317999964, 35.149184278111584 ], [ -80.912328917999957, 35.14913887811155 ], [ -80.912133584999935, 35.149001478111543 ], [ -80.912189117999944, 35.148108211111548 ], [ -80.912077317999945, 35.147856411111569 ], [ -80.912049317999958, 35.14742121111157 ], [ -80.912383717999944, 35.146825478111566 ], [ -80.912801784999942, 35.146435878111589 ], [ -80.913024717999974, 35.146000811111591 ], [ -80.913052317999984, 35.145542611111615 ], [ -80.91252211799997, 35.144626811111614 ], [ -80.912493784999981, 35.14380221111157 ], [ -80.912883784999963, 35.14290881111161 ], [ -80.912883584999975, 35.142267478111677 ], [ -80.912716184999979, 35.141969811111672 ], [ -80.911684184999956, 35.141328878111629 ], [ -80.911377317999964, 35.140985411111629 ], [ -80.911042317999943, 35.140138011111624 ], [ -80.911041917999981, 35.139038611111651 ], [ -80.911125517999949, 35.138855411111649 ], [ -80.911487917999978, 35.1388324111117 ], [ -80.911738917999969, 35.138992611111597 ], [ -80.911878517999966, 35.139221678111596 ], [ -80.913272584999959, 35.139335678111593 ], [ -80.913607117999959, 35.139129411111618 ], [ -80.913746517999982, 35.138854478111604 ], [ -80.914248184999963, 35.13873987811165 ], [ -80.91455478499995, 35.138556478111667 ], [ -80.914889184999936, 35.138121211111653 ], [ -80.915056317999984, 35.137686011111768 ], [ -80.915167384999961, 35.13688427811168 ], [ -80.915585517999929, 35.136563478111682 ], [ -80.915585517999929, 35.136403211111691 ], [ -80.915194917999941, 35.136014011111669 ], [ -80.915194784999983, 35.135807878111642 ], [ -80.915584984999967, 35.135395411111702 ], [ -80.916170384999987, 35.135303678111683 ], [ -80.916393317999962, 35.134960011111737 ], [ -80.916420784999957, 35.134364478111728 ], [ -80.916755317999957, 35.134204078111686 ], [ -80.916782984999941, 35.133677278111648 ], [ -80.917117385, 35.13331067811167 ], [ -80.91722871799999, 35.132944211111642 ], [ -80.917953584999964, 35.13289807811163 ], [ -80.918176517999939, 35.132760611111713 ], [ -80.918594384999949, 35.132073411111733 ], [ -80.918817317999981, 35.131913011111692 ], [ -80.919207317999962, 35.131294411111739 ], [ -80.919597584999963, 35.131225611111688 ], [ -80.919876784999985, 35.131958278111746 ], [ -80.920099917999949, 35.13209567811171 ], [ -80.920462318, 35.132118478111778 ], [ -80.920685318, 35.131981011111769 ] ] } }, +{ "type": "Feature", "id": "fp-88", "properties": { "length_km": 2.9260609756749041, "slope_percent": 0.00177033, "main_id": 54, "toid": "fp-89", "realized_catchment": "cat-88" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.838756184999966, 35.109974211111982 ], [ -80.839731784999969, 35.109882878111911 ], [ -80.840790984999956, 35.109493878112083 ], [ -80.841934117999983, 35.108784211111988 ], [ -80.842798184999936, 35.108647011111941 ], [ -80.843550917999949, 35.108303611112014 ], [ -80.843773917999954, 35.108120411112004 ], [ -80.843997117999947, 35.107731011112051 ], [ -80.843858317999945, 35.106288078111916 ], [ -80.844862184999954, 35.104845278112087 ], [ -80.845196917999942, 35.103952211112031 ], [ -80.846898117999942, 35.100837678112036 ], [ -80.848487584999987, 35.097814678112158 ], [ -80.849407784999983, 35.096326078112106 ], [ -80.851944584999956, 35.09312001111217 ], [ -80.852279117999956, 35.092822411112145 ], [ -80.852780917999951, 35.092158211112164 ], [ -80.853561517999935, 35.091036078112168 ], [ -80.853756717999943, 35.090646811112151 ], [ -80.854481384999985, 35.089799478112191 ], [ -80.855233917999954, 35.089295678112244 ], [ -80.855504187407163, 35.089162444289279 ] ] } }, +{ "type": "Feature", "id": "fp-89", "properties": { "length_km": 2.9260609756749605, "slope_percent": 0.00177033, "main_id": 54, "toid": "fp-77", "realized_catchment": "cat-89" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.855504187407163, 35.089162444289279 ], [ -80.856766784999934, 35.088540011112244 ], [ -80.857184717999985, 35.088219478112222 ], [ -80.857435717999962, 35.087738478112271 ], [ -80.857854184999951, 35.084921411112234 ], [ -80.858662517999974, 35.083661811112236 ], [ -80.859721517999958, 35.082814411112274 ], [ -80.860055917999944, 35.08237927811237 ], [ -80.860223317999967, 35.081898278112241 ], [ -80.860279317999982, 35.080386611112246 ], [ -80.862397384999952, 35.076859678112335 ], [ -80.862564517999942, 35.076401611112374 ], [ -80.863539784999944, 35.075119078112344 ], [ -80.86429211799998, 35.07475261111243 ], [ -80.864877317999969, 35.074363411112408 ], [ -80.866382184999964, 35.071454678112374 ], [ -80.867218117999982, 35.070149078112465 ], [ -80.867329584999936, 35.068820678112388 ], [ -80.867663984999979, 35.068087811112434 ], [ -80.869224184999965, 35.066507478112477 ] ] } }, +{ "type": "Feature", "id": "fp-90", "properties": { "length_km": 4.0465456949400362, "slope_percent": 0.00242174, "main_id": 12, "toid": "fp-91", "realized_catchment": "cat-90" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.810353517999943, 35.250132878110342 ], [ -80.81247618499998, 35.249080278110519 ], [ -80.813621717999979, 35.247912811110474 ], [ -80.814320584999962, 35.246768011110511 ], [ -80.814432584999963, 35.246378678110503 ], [ -80.814014317999977, 35.245760078110443 ], [ -80.814014584999939, 35.245462411110438 ], [ -80.815159784999935, 35.244523878110492 ], [ -80.815243984999938, 35.243951411110515 ], [ -80.81530031799997, 35.243310078110476 ], [ -80.815160917999947, 35.243081011110505 ], [ -80.815133384999967, 35.242760278110431 ], [ -80.81563618499996, 35.242096411110545 ], [ -80.816390917999968, 35.240859878110442 ], [ -80.816893784999962, 35.24028761111051 ], [ -80.818178917999944, 35.239005611110507 ], [ -80.818458317999955, 35.238570478110482 ], [ -80.818486517999986, 35.23824987811058 ], [ -80.818291517999967, 35.237700078110564 ], [ -80.818096317999945, 35.237425211110533 ], [ -80.818096517999948, 35.237127411110663 ], [ -80.818236317999947, 35.236852678110516 ], [ -80.819493117999968, 35.236005811110616 ], [ -80.819688717999952, 35.23566241111066 ], [ -80.82005191799999, 35.235296078110593 ], [ -80.822034784999971, 35.233922811110617 ], [ -80.82267731799999, 35.233258811110574 ], [ -80.822956784999974, 35.232617611110655 ], [ -80.822817384999951, 35.232342811110648 ], [ -80.822315317999937, 35.231770011110591 ], [ -80.822203917999957, 35.231403611110707 ], [ -80.822204318, 35.230693478110574 ], [ -80.822567517999971, 35.230281278110603 ], [ -80.823349184999984, 35.230052611110693 ], [ -80.823572784999953, 35.229686278110663 ], [ -80.823601184999973, 35.22899921111069 ], [ -80.823824517999981, 35.228816011110659 ], [ -80.825695318, 35.227763278110679 ], [ -80.826700317999951, 35.227672011110592 ], [ -80.827230784999983, 35.227489011110613 ], [ -80.827593917999934, 35.227008211110693 ], [ -80.828432917999962, 35.224145678110673 ], [ -80.828545317999954, 35.222954611110652 ], [ -80.82882458499995, 35.222496611110721 ], [ -80.829131717999985, 35.222267811110747 ], [ -80.829764944800004, 35.221418395814041 ] ] } }, +{ "type": "Feature", "id": "fp-91", "properties": { "length_km": 4.0465456949400647, "slope_percent": 0.00242174, "main_id": 12, "toid": "fp-32", "realized_catchment": "cat-91" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.829764944800004, 35.221418395814041 ], [ -80.830667984999934, 35.22020701111083 ], [ -80.830835717999946, 35.219726078110803 ], [ -80.830836117999979, 35.219016078110762 ], [ -80.831143184999959, 35.218741411110734 ], [ -80.833543984999949, 35.217757278110803 ], [ -80.835051584999974, 35.216750011110783 ], [ -80.835442717999967, 35.216017278110776 ], [ -80.835610717999941, 35.214895078110786 ], [ -80.836169584999965, 35.213314878110914 ], [ -80.836589117999949, 35.211391211110822 ], [ -80.836562117999975, 35.20965047811093 ], [ -80.836339517999988, 35.208138678110885 ], [ -80.836563384999977, 35.206902078110907 ], [ -80.836703784999941, 35.205046878110927 ], [ -80.837067584999943, 35.20291701111104 ], [ -80.83732011799998, 35.200329011110981 ], [ -80.837935517999938, 35.196939411111046 ], [ -80.838047184999937, 35.196710478111036 ], [ -80.838828717999945, 35.195977811110986 ], [ -80.840726917999973, 35.19400861111103 ], [ -80.842597184999988, 35.191901878111054 ], [ -80.843434584999954, 35.190573678111122 ], [ -80.843686184999953, 35.189589011111103 ], [ -80.843995024567377, 35.189094944254023 ] ] } }, +{ "type": "Feature", "id": "fp-92", "properties": { "length_km": 3.7318622861130137, "slope_percent": 0.00562756, "main_id": 121, "toid": "fp-93", "realized_catchment": "cat-92" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.905978584999943, 35.246389211110447 ], [ -80.907066984999972, 35.24526661111053 ], [ -80.907904384999938, 35.244648011110471 ], [ -80.90885331799997, 35.244098011110495 ], [ -80.909020717999965, 35.243823078110545 ], [ -80.909271584999942, 35.242792411110479 ], [ -80.910331984999971, 35.241807278110521 ], [ -80.910555184999964, 35.241440811110607 ], [ -80.910582717999944, 35.240501678110519 ], [ -80.910358917999986, 35.239425278110545 ], [ -80.90980051799994, 35.23910481111055 ], [ -80.90977238499994, 35.238852878110542 ], [ -80.909911917999978, 35.238578011110562 ], [ -80.910860718, 35.237753211110601 ], [ -80.911641917999972, 35.236607811110588 ], [ -80.91278538499995, 35.23427127811059 ], [ -80.913008317999982, 35.233401011110622 ], [ -80.913873184999943, 35.232461611110573 ], [ -80.914682384999949, 35.231934611110596 ], [ -80.916328784999962, 35.230582678110594 ], [ -80.917026184999941, 35.229826611110667 ], [ -80.917053917999965, 35.229528811110676 ], [ -80.917193317999988, 35.22925401111074 ], [ -80.918225584999945, 35.228245811110689 ], [ -80.918420784999967, 35.22776481111061 ], [ -80.919090184999959, 35.226962878110676 ], [ -80.919480917999977, 35.226733811110691 ], [ -80.919927517999952, 35.226619078110694 ], [ -80.920262384999944, 35.226710611110654 ], [ -80.920625317999963, 35.226710478110597 ], [ -80.920736784999974, 35.226595811110656 ], [ -80.920764584999986, 35.226229411110744 ], [ -80.920540917999958, 35.225656878110691 ], [ -80.920484717999955, 35.225015611110642 ], [ -80.92065191799999, 35.224443011110701 ], [ -80.921265584999958, 35.223870078110707 ], [ -80.921655984999973, 35.223205811110752 ], [ -80.92173918499999, 35.222106411110744 ], [ -80.92238038499994, 35.220869411110677 ], [ -80.923300984999969, 35.220181811110876 ], [ -80.924491553341795, 35.21951175402814 ] ] } }, +{ "type": "Feature", "id": "fp-93", "properties": { "length_km": 3.7318622861129289, "slope_percent": 0.00562756, "main_id": 121, "toid": "fp-86", "realized_catchment": "cat-93" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.924491553341795, 35.21951175402814 ], [ -80.924807717999954, 35.219333811110772 ], [ -80.925588717999986, 35.218577678110776 ], [ -80.925616384999941, 35.21823407811074 ], [ -80.925420784999986, 35.217799011110785 ], [ -80.925196984999971, 35.216791278110804 ], [ -80.924973584999961, 35.216516611110805 ], [ -80.924470984999971, 35.216196211110841 ], [ -80.923773184999973, 35.216081878110877 ], [ -80.922041984999979, 35.214616811110872 ], [ -80.922097517999987, 35.213861011110808 ], [ -80.922320384999978, 35.213357011110872 ], [ -80.922570917999963, 35.212326278110787 ], [ -80.923407784999924, 35.211570078110874 ], [ -80.923435317999974, 35.211111878110842 ], [ -80.923519117999945, 35.210997411110839 ], [ -80.92351851799998, 35.209966678110838 ], [ -80.922624785, 35.208753411110905 ], [ -80.922624384999963, 35.208226611110845 ], [ -80.922708117999946, 35.208112011110998 ], [ -80.922735717999956, 35.207631011110884 ], [ -80.922874984999964, 35.207264478110893 ], [ -80.922902517999944, 35.206302478110899 ], [ -80.923153384999978, 35.205798611110922 ], [ -80.923153117999959, 35.205409211110897 ], [ -80.922873984999967, 35.205134478110907 ], [ -80.922482784999929, 35.204378811110971 ], [ -80.921952584999985, 35.204379011110859 ], [ -80.92083591799998, 35.203348811110949 ], [ -80.920807784999965, 35.203051011110951 ], [ -80.920556384999941, 35.202593078110901 ], [ -80.920025984999938, 35.202112278110995 ], [ -80.919886317999953, 35.201837478110967 ], [ -80.919969917999936, 35.201539678111004 ], [ -80.920471717999988, 35.200829478110926 ], [ -80.920638784999937, 35.200371411110957 ], [ -80.921001318, 35.199798678111009 ], [ -80.921224384999974, 35.199615278111018 ], [ -80.921475184999963, 35.199065611110925 ], [ -80.921474917999944, 35.19840141111105 ], [ -80.92139098499996, 35.198103611111001 ], [ -80.920860384999969, 35.197233611110953 ], [ -80.920636784999942, 35.196569411111064 ], [ -80.919631584999919, 35.195401611111009 ], [ -80.919324584999984, 35.194897878110986 ], [ -80.91882211799998, 35.194440011110999 ], [ -80.918208184999969, 35.194325678110992 ], [ -80.917315517999953, 35.194509278111006 ], [ -80.916701717999956, 35.194761478111069 ], [ -80.915976384999965, 35.194876211111037 ], [ -80.915641517999973, 35.194761878111088 ], [ -80.915306584999939, 35.194532878111048 ], [ -80.915027184999985, 35.19366267811106 ] ] } }, +{ "type": "Feature", "id": "fp-94", "properties": { "length_km": 3.0964450981277749, "slope_percent": 0.00142257, "main_id": 1, "toid": "fp-95", "realized_catchment": "cat-94" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.869099518, 35.225505278110731 ], [ -80.869127517999971, 35.224612078110688 ], [ -80.869015917999945, 35.224337211110729 ], [ -80.868960184999935, 35.223375278110723 ], [ -80.869322984999926, 35.222253078110732 ], [ -80.870607117999953, 35.220856011110833 ], [ -80.87150031799996, 35.220031478110776 ], [ -80.872086317999958, 35.21922987811076 ], [ -80.872700517999988, 35.217580878110766 ], [ -80.872728384999945, 35.217099878110837 ], [ -80.873202917999947, 35.215565411110752 ], [ -80.873761117999948, 35.214878278110824 ], [ -80.874793584999964, 35.214466011110815 ], [ -80.876244917999941, 35.213710078110758 ], [ -80.877054184999963, 35.212862678110895 ], [ -80.87786351799997, 35.212221411110825 ], [ -80.879370384999959, 35.211282278110822 ], [ -80.880012317999956, 35.210595211110842 ], [ -80.881016784999986, 35.209472878110873 ], [ -80.88327718499994, 35.207915211110844 ], [ -80.884309585, 35.206999011110845 ], [ -80.885369984999954, 35.206976011110932 ], [ -80.885955984999953, 35.207090478110892 ], [ -80.88659791799995, 35.207708811110933 ], [ -80.88763051799998, 35.208166811110871 ], [ -80.888914317999919, 35.208418678110881 ], [ -80.889042911529472, 35.208421493429739 ] ] } }, +{ "type": "Feature", "id": "fp-95", "properties": { "length_km": 3.0964450981277221, "slope_percent": 0.00142257, "main_id": 1, "toid": "fp-86", "realized_catchment": "cat-95" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.889042911529472, 35.208421493429739 ], [ -80.88994678499995, 35.208441278110882 ], [ -80.890253584999982, 35.208258011110857 ], [ -80.891592784999943, 35.206654611111006 ], [ -80.892513584999961, 35.205852878110896 ], [ -80.89268091799994, 35.205578011110902 ], [ -80.893015717999958, 35.20537187811091 ], [ -80.893741317999954, 35.205096878110908 ], [ -80.894159717999969, 35.204753278110879 ], [ -80.89499658499993, 35.203630811110912 ], [ -80.896419384999945, 35.202393678111029 ], [ -80.897088917999952, 35.201592011110954 ], [ -80.89775838499996, 35.201019278110905 ], [ -80.898372317999971, 35.200698611111001 ], [ -80.899767317999988, 35.200308878110931 ], [ -80.900715917999946, 35.199759011110999 ], [ -80.902836184999956, 35.198934078110959 ], [ -80.904426384999965, 35.198223611111061 ], [ -80.90649078499996, 35.196940411110994 ], [ -80.906992784999957, 35.196505078111016 ], [ -80.908303384999968, 35.194649611111018 ], [ -80.908442717999947, 35.194099878111025 ], [ -80.909530717999985, 35.193664411111023 ], [ -80.910060717999954, 35.19359547811105 ], [ -80.910535184999958, 35.193847278111001 ], [ -80.911148984999954, 35.193892878111036 ], [ -80.911957984999958, 35.193663678111072 ], [ -80.913185784999925, 35.193869411111024 ], [ -80.914525117999972, 35.193846078110994 ], [ -80.915027184999985, 35.19366267811106 ] ] } }, +{ "type": "Feature", "id": "fp-96", "properties": { "length_km": 3.5687837086343031, "slope_percent": 0.00579278, "main_id": 1, "toid": "fp-97", "realized_catchment": "cat-96" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.836321584999951, 35.307382611109766 ], [ -80.835874984999975, 35.306764211109801 ], [ -80.835539717999964, 35.306535078109832 ], [ -80.833053317999941, 35.306099011109822 ], [ -80.832718117999946, 35.305938611109895 ], [ -80.83160111799998, 35.304999211109774 ], [ -80.830679317999966, 35.304517811109733 ], [ -80.829478384999945, 35.303601278109838 ], [ -80.829031784999984, 35.302959811109908 ], [ -80.828082184999971, 35.302318278109858 ], [ -80.828082384999959, 35.302020478109895 ], [ -80.828474584999981, 35.300280011109805 ], [ -80.828475117999986, 35.299295078109921 ], [ -80.828223984999966, 35.298859811109899 ], [ -80.827944717999969, 35.298630811109874 ], [ -80.826632117999964, 35.298103611109923 ], [ -80.825793784999973, 35.298400878109902 ], [ -80.825682117999975, 35.298332078109887 ], [ -80.825626384999964, 35.297942811109877 ], [ -80.825347317999956, 35.297415878109923 ], [ -80.825319584999974, 35.297186878109855 ], [ -80.825235784999961, 35.297141078109952 ], [ -80.825236317999952, 35.296316478109972 ], [ -80.82501291799997, 35.296018678109917 ], [ -80.824678117999952, 35.295194011109906 ], [ -80.824510584999985, 35.295102411109951 ], [ -80.824035717999948, 35.29510207810992 ], [ -80.823700917999986, 35.294369078109895 ], [ -80.823227117999977, 35.292628278109987 ], [ -80.822919984999956, 35.292399078109966 ], [ -80.822333517999937, 35.292146878109968 ], [ -80.821383784999981, 35.292100678110067 ], [ -80.821020784999959, 35.291848611109998 ], [ -80.821048984999948, 35.291527878109989 ], [ -80.821132717999944, 35.291436411109892 ], [ -80.821133317999966, 35.290611811109983 ], [ -80.821049584999969, 35.290588878109958 ], [ -80.820937984999958, 35.290337011110033 ], [ -80.820463184999937, 35.290130611110023 ], [ -80.819904984999937, 35.289534878109968 ], [ -80.819681784999943, 35.289168278110012 ], [ -80.819626184999947, 35.288687278109947 ], [ -80.819514717999937, 35.288389478110012 ], [ -80.818900384999949, 35.28799987811 ], [ -80.818956784999955, 35.287312811109963 ], [ -80.819460184999969, 35.28625947811004 ], [ -80.819516517999944, 35.285801478110074 ], [ -80.819098317999973, 35.28461027811003 ], [ -80.819070717999978, 35.284060611110085 ], [ -80.819025995608158, 35.283876652913463 ] ] } }, +{ "type": "Feature", "id": "fp-97", "properties": { "length_km": 3.5687837086343728, "slope_percent": 0.00579278, "main_id": 1, "toid": "fp-98", "realized_catchment": "cat-97" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.819025995608158, 35.283876652913463 ], [ -80.818987184999969, 35.283717011110078 ], [ -80.818428985, 35.282960878109996 ], [ -80.818345517999944, 35.282342478110088 ], [ -80.818625517999976, 35.281495211110069 ], [ -80.818681717999937, 35.28101427811005 ], [ -80.82035851799999, 35.279480478110052 ], [ -80.820945384999959, 35.279022678110124 ], [ -80.821336717999941, 35.278541878110119 ], [ -80.822510384999987, 35.277649211110088 ], [ -80.823040984999977, 35.277603611110166 ], [ -80.823627517999967, 35.277855811110193 ], [ -80.824493184999938, 35.277970678110151 ], [ -80.825611117999983, 35.276711411110149 ], [ -80.825806717999967, 35.276322211110092 ], [ -80.825862917999984, 35.275864011110137 ], [ -80.826142584999957, 35.275108278110181 ], [ -80.826198984999962, 35.274306678110179 ], [ -80.825892117999956, 35.273734011110179 ], [ -80.825864717999949, 35.273001078110163 ], [ -80.826144517999978, 35.272176678110164 ], [ -80.826843317999987, 35.271054678110168 ], [ -80.828296384999987, 35.269360411110185 ], [ -80.828603784999984, 35.269108678110229 ], [ -80.830307717999972, 35.268216011110162 ], [ -80.831285384999958, 35.267849878110219 ], [ -80.832095584999934, 35.267231811110243 ], [ -80.832486784999958, 35.26670507811022 ], [ -80.833688184999986, 35.265606211110253 ], [ -80.834051517999967, 35.265125278110311 ], [ -80.834275317999982, 35.264300878110319 ], [ -80.834526917999938, 35.263865811110307 ], [ -80.834861984999975, 35.263499411110324 ], [ -80.83609111799997, 35.262652411110295 ], [ -80.837403784999935, 35.261988611110297 ], [ -80.839973317999977, 35.260912878110311 ] ] } }, +{ "type": "Feature", "id": "fp-98", "properties": { "length_km": 2.5460717335502205, "slope_percent": 0.00241948, "main_id": 1, "toid": "fp-99", "realized_catchment": "cat-98" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.839973317999977, 35.260912878110311 ], [ -80.840252917999976, 35.260317411110272 ], [ -80.840336984999965, 35.259584478110405 ], [ -80.840588517999947, 35.2590120111103 ], [ -80.840867917999958, 35.258714411110404 ], [ -80.841929117999925, 35.258073411110288 ], [ -80.84243218499995, 35.256882478110356 ], [ -80.842711784999963, 35.255806211110283 ], [ -80.844695584999954, 35.252394011110383 ], [ -80.844947184999953, 35.251409211110357 ], [ -80.84567231799997, 35.25014621111049 ], [ -80.846370984999965, 35.24831421111044 ], [ -80.847180917999935, 35.247489811110398 ], [ -80.847767517999955, 35.247077678110394 ], [ -80.848102584999936, 35.246665478110465 ], [ -80.848968584999966, 35.24522281111043 ], [ -80.849638917999982, 35.244283811110471 ], [ -80.849750784999969, 35.243619611110418 ], [ -80.850002317999952, 35.243047078110507 ], [ -80.850393184999973, 35.242703611110457 ], [ -80.851817517999962, 35.241054811110502 ], [ -80.851834487022629, 35.240777909648841 ] ] } }, +{ "type": "Feature", "id": "fp-99", "properties": { "length_km": 2.5460717335503227, "slope_percent": 0.00241948, "main_id": 1, "toid": "fp-94", "realized_catchment": "cat-99" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.851834487022629, 35.240777909648841 ], [ -80.851845584999978, 35.240596811110528 ], [ -80.851370984999946, 35.240207411110582 ], [ -80.851343184999962, 35.23997827811062 ], [ -80.851873784999952, 35.239497478110536 ], [ -80.852543984999969, 35.238673011110492 ], [ -80.85307478499999, 35.237573811110558 ], [ -80.855113184999936, 35.23539827811063 ], [ -80.856174317999944, 35.2341846111105 ], [ -80.856565184999965, 35.233497478110628 ], [ -80.857682117999957, 35.232077678110613 ], [ -80.859217717999968, 35.230405878110645 ], [ -80.859301584999969, 35.22967301111057 ], [ -80.859636718, 35.229008811110624 ], [ -80.86013918499998, 35.228734078110634 ], [ -80.861479117999963, 35.228757078110668 ], [ -80.862037317999977, 35.228574011110588 ], [ -80.862483984999983, 35.228138811110668 ], [ -80.864131117999989, 35.226810611110679 ], [ -80.86457771799995, 35.22660447811068 ], [ -80.866894384999966, 35.226055011110709 ], [ -80.868373784999946, 35.225963478110671 ], [ -80.869099518, 35.225505278110731 ] ] } }, +{ "type": "Feature", "id": "fp-100", "properties": { "length_km": 4.913449989482416, "slope_percent": 0.00232481, "main_id": 81, "toid": "fp-101", "realized_catchment": "cat-100" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.939489517999959, 35.184834011111143 ], [ -80.939237784999989, 35.184124078111218 ], [ -80.939098184999978, 35.183918078111134 ], [ -80.938986184999976, 35.183483078111188 ], [ -80.93879071799995, 35.183185411111168 ], [ -80.938538517999987, 35.181628078111196 ], [ -80.937840517999973, 35.180758078111189 ], [ -80.937142184999985, 35.179613278111226 ], [ -80.936750984999961, 35.178834678111137 ], [ -80.936442917999955, 35.177117211111216 ], [ -80.936219384999958, 35.176773678111289 ], [ -80.935744917999955, 35.176315811111195 ], [ -80.935577317999957, 35.175949411111269 ], [ -80.93557671799995, 35.175079078111274 ], [ -80.935799517999953, 35.174506411111153 ], [ -80.935910317999969, 35.173498611111278 ], [ -80.935937317999972, 35.172376278111258 ], [ -80.935406717999953, 35.171529078111298 ], [ -80.934234384999954, 35.170522011111373 ], [ -80.934234384999954, 35.170338811111222 ], [ -80.934652584999967, 35.17013241111129 ], [ -80.934763917999987, 35.169880411111272 ], [ -80.934344717999977, 35.168712478111352 ], [ -80.934149317999982, 35.168506478111361 ], [ -80.934204584999975, 35.167750611111423 ], [ -80.933534584999961, 35.166949278111325 ], [ -80.932780984999965, 35.16623967811131 ], [ -80.932027784999946, 35.165965278111329 ], [ -80.93177658499998, 35.165759211111343 ], [ -80.93158111799994, 35.165347078111367 ], [ -80.930911317999971, 35.164774678111321 ], [ -80.930687984999963, 35.164362611111365 ], [ -80.930659917999947, 35.164110678111385 ], [ -80.930576184999964, 35.164042011111363 ], [ -80.930575984999962, 35.163629678111391 ], [ -80.930770917999951, 35.16314867811144 ], [ -80.931133117999934, 35.162667478111395 ], [ -80.931160717999944, 35.162278078111342 ], [ -80.931076984999947, 35.162209411111355 ], [ -80.931076784999959, 35.161820078111404 ], [ -80.93258071799994, 35.158727411111457 ], [ -80.932858317999944, 35.156940811111504 ], [ -80.932912717999955, 35.154971011111463 ], [ -80.932549317999985, 35.153803078111544 ], [ -80.932270184999936, 35.1534138781115 ], [ -80.931740184999967, 35.153253811111497 ], [ -80.931042584999943, 35.152635811111516 ], [ -80.930149917999984, 35.151971878111461 ], [ -80.930038184999944, 35.151697078111539 ], [ -80.930232784999987, 35.15091827811154 ], [ -80.930232384999954, 35.150277011111562 ], [ -80.929785917999936, 35.149750411111512 ], [ -80.929032584999959, 35.149178078111554 ], [ -80.92833498499995, 35.148422611111528 ], [ -80.928055717999939, 35.147873078111623 ], [ -80.927552784999989, 35.146293011111595 ], [ -80.926911184999938, 35.145858211111587 ] ] } }, +{ "type": "Feature", "id": "fp-101", "properties": { "length_km": 1.7793006398505817, "slope_percent": 0.00232481, "main_id": 81, "toid": "fp-84", "realized_catchment": "cat-101" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.926911184999938, 35.145858211111587 ], [ -80.92638091799995, 35.145148411111585 ], [ -80.926017984999987, 35.144392678111565 ], [ -80.925543184999981, 35.143293478111616 ], [ -80.924622117999959, 35.141667678111617 ], [ -80.924593917999971, 35.14107221111157 ], [ -80.924927984999954, 35.140155878111635 ], [ -80.924843984999939, 35.139697878111662 ], [ -80.92397871799993, 35.137934611111554 ], [ -80.923197718, 35.137362411111702 ], [ -80.922583717999942, 35.136423478111638 ], [ -80.922527517999981, 35.135690611111741 ], [ -80.922192717999962, 35.135209811111672 ], [ -80.922164185, 35.134270678111726 ], [ -80.922526384999969, 35.133583411111694 ], [ -80.922526117999965, 35.133239878111731 ], [ -80.921940584999959, 35.133171411111647 ], [ -80.92160578499994, 35.132828011111712 ], [ -80.921159584999941, 35.132622011111735 ], [ -80.921131517999981, 35.132209811111665 ], [ -80.920685318, 35.131981011111769 ] ] } }, +{ "type": "Feature", "id": "fp-102", "properties": { "length_km": 2.5545587936122018, "slope_percent": 0.00531919, "main_id": 113, "toid": "fp-103", "realized_catchment": "cat-102" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.732862117999943, 35.226306678110689 ], [ -80.735510784999974, 35.224151411110618 ], [ -80.73576058499998, 35.223074678110613 ], [ -80.736289384999964, 35.221952011110766 ], [ -80.736372317999965, 35.221379278110774 ], [ -80.736957117999978, 35.220348078110817 ], [ -80.737207117999958, 35.21947761111084 ], [ -80.737149785, 35.218309611110747 ], [ -80.736840717999939, 35.216775278110831 ], [ -80.736558984999988, 35.214851678110833 ], [ -80.736837117999983, 35.21416427811085 ], [ -80.737391917999972, 35.2116902781109 ], [ -80.737809517999949, 35.210979878110848 ], [ -80.737864584999969, 35.210407211110855 ], [ -80.738031517999943, 35.210040678110829 ], [ -80.738784517999989, 35.209673478110886 ], [ -80.738700184999971, 35.209261278110908 ], [ -80.738699717999964, 35.20887201111082 ], [ -80.738810985, 35.208665678110869 ], [ -80.739368584999966, 35.208321678110899 ], [ -80.739954317999945, 35.208137878110861 ], [ -80.740316717999974, 35.207816878110911 ], [ -80.740316184999983, 35.207519211110949 ], [ -80.740120184999967, 35.206969611110914 ], [ -80.740259317999971, 35.206671811110873 ], [ -80.741000987845396, 35.206131011774275 ] ] } }, +{ "type": "Feature", "id": "fp-103", "properties": { "length_km": 2.5545587936120349, "slope_percent": 0.00531919, "main_id": 113, "toid": "fp-104", "realized_catchment": "cat-103" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.741000987845396, 35.206131011774275 ], [ -80.741485917999967, 35.20577741111093 ], [ -80.741484784999955, 35.205021611110915 ], [ -80.741372184999989, 35.204334611110909 ], [ -80.741288384999962, 35.204265878110945 ], [ -80.741260117999943, 35.203945278110908 ], [ -80.741176317999987, 35.203876678110873 ], [ -80.74095111799997, 35.202456878110993 ], [ -80.740918385, 35.199135878110987 ], [ -80.742394317999981, 35.197004478110991 ], [ -80.742589384999974, 35.196821078111043 ], [ -80.743368984999961, 35.195698011110956 ], [ -80.743702317999961, 35.194667078111053 ], [ -80.743450117999942, 35.19388867811108 ], [ -80.743366384999945, 35.193842878111084 ], [ -80.743252584999937, 35.192354211111095 ], [ -80.74308471799999, 35.19194221111109 ], [ -80.74277731799998, 35.191599011111037 ], [ -80.742302717999962, 35.191393278111057 ], [ -80.741827784999941, 35.190981411111061 ], [ -80.741464317999942, 35.190455011111048 ], [ -80.740208584999962, 35.190318678111005 ], [ -80.739845784999943, 35.190181678111067 ], [ -80.73900791799997, 35.189541211111049 ], [ -80.738449117999949, 35.188992011111004 ], [ -80.738309184999935, 35.188671411111116 ], [ -80.738168384999938, 35.187663811111086 ], [ -80.738000584999952, 35.187457878111132 ], [ -80.737498117999962, 35.187183478111137 ], [ -80.736772517999952, 35.186932211111149 ] ] } }, +{ "type": "Feature", "id": "fp-104", "properties": { "length_km": 3.8126861524129567, "slope_percent": 0.00531919, "main_id": 113, "toid": "fp-28", "realized_catchment": "cat-104" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.736772517999952, 35.186932211111149 ], [ -80.735878984999957, 35.186337611111114 ], [ -80.735570784999965, 35.185421678111091 ], [ -80.735542117999941, 35.184734611111153 ], [ -80.735625517999949, 35.184551278111222 ], [ -80.73654611799999, 35.184527611111157 ], [ -80.737159317999954, 35.18418347811123 ], [ -80.738219717999982, 35.184434478111228 ], [ -80.738666117999969, 35.184434011111151 ], [ -80.738889117999975, 35.184342211111165 ], [ -80.738915984999949, 35.183609278111163 ], [ -80.738831517999984, 35.183036678111165 ], [ -80.738383784999982, 35.182052211111149 ], [ -80.738438717999941, 35.181342078111193 ], [ -80.738270717999967, 35.180930078111224 ], [ -80.738270317999962, 35.180586478111145 ], [ -80.738632317999986, 35.180128011111172 ], [ -80.738631984999984, 35.179944811111234 ], [ -80.738129517999937, 35.179647611111207 ], [ -80.738017584999966, 35.179372811111151 ], [ -80.737515117999976, 35.179167211111228 ], [ -80.736455317999969, 35.179236811111217 ], [ -80.736148317999948, 35.179145478111138 ], [ -80.735645717999944, 35.178756611111233 ], [ -80.73533798499993, 35.178184278111267 ], [ -80.735309517999966, 35.17768041111119 ], [ -80.735420118, 35.176947411111264 ], [ -80.736031784999952, 35.17552681111124 ], [ -80.735835984999952, 35.175091878111253 ], [ -80.735835518, 35.174702478111328 ], [ -80.736030384999935, 35.174381611111265 ], [ -80.736196917999962, 35.173763078111179 ], [ -80.736139984999966, 35.172984478111289 ], [ -80.736389784999972, 35.172136811111343 ], [ -80.736193784999955, 35.171587278111232 ], [ -80.735942384999987, 35.171243878111227 ], [ -80.735746384999985, 35.170671478111316 ], [ -80.735047717999976, 35.16966441111127 ], [ -80.735047184999985, 35.169229211111322 ], [ -80.735297517999982, 35.168702278111347 ], [ -80.735520584999975, 35.168587478111306 ], [ -80.736635917999948, 35.168449078111301 ], [ -80.737052917999961, 35.167395211111291 ], [ -80.73746878499999, 35.165585411111351 ], [ -80.737523717999977, 35.164944011111338 ], [ -80.737857984999934, 35.164623078111433 ], [ -80.738080384999989, 35.164073211111365 ], [ -80.73807998499997, 35.1637982781114 ], [ -80.737772384999971, 35.16327187811136 ], [ -80.736712384999962, 35.163112478111401 ], [ -80.73623811799996, 35.162975478111441 ], [ -80.735623984999961, 35.162586678111417 ], [ -80.735288717999936, 35.162128878111403 ], [ -80.735064984999951, 35.161602411111346 ], [ -80.735426717999985, 35.161052278111399 ], [ -80.73545418499998, 35.160823278111401 ] ] } }, +{ "type": "Feature", "id": "fp-105", "properties": { "length_km": 0.92772541866864988, "slope_percent": 0.00139558, "main_id": 24, "toid": "fp-106", "realized_catchment": "cat-105" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.812832384999979, 35.076501278112381 ], [ -80.812971717999972, 35.076501411112353 ], [ -80.814615984999989, 35.075838011112303 ], [ -80.81559118499996, 35.075792611112298 ], [ -80.816036984999968, 35.075884478112286 ], [ -80.816761184999962, 35.07615967811229 ], [ -80.817986317999967, 35.077145078112281 ], [ -80.818348384999979, 35.077328478112335 ], [ -80.819462784999985, 35.077466411112304 ], [ -80.82057738499999, 35.077260678112353 ], [ -80.821552717999964, 35.076986278112336 ], [ -80.822137917999967, 35.076963678112314 ] ] } }, +{ "type": "Feature", "id": "fp-106", "properties": { "length_km": 2.0604284974464351, "slope_percent": 0.00139558, "main_id": 24, "toid": "fp-25", "realized_catchment": "cat-106" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.822137917999967, 35.076963678112314 ], [ -80.822527717999947, 35.077261611112306 ], [ -80.82425511799994, 35.077216478112376 ], [ -80.824394584999936, 35.077102078112326 ], [ -80.824589784999944, 35.076621211112332 ], [ -80.824617984999975, 35.076231811112365 ], [ -80.824896984999953, 35.075476078112402 ], [ -80.826653384999986, 35.07357567811237 ], [ -80.826987784999972, 35.073369678112435 ], [ -80.827377917999968, 35.073347011112382 ], [ -80.827907117999985, 35.073484611112434 ], [ -80.828742984999948, 35.073484878112346 ], [ -80.829467384999987, 35.073233278112419 ], [ -80.83033118499999, 35.073142011112417 ], [ -80.833200517999956, 35.073532278112452 ], [ -80.834008317999974, 35.073738678112356 ], [ -80.834871784999962, 35.074128278112326 ], [ -80.835846584999956, 35.074770011112335 ], [ -80.837211517999947, 35.075251278112319 ], [ -80.837852317999989, 35.075366078112317 ], [ -80.838493117999974, 35.075251811112373 ], [ -80.839022717999967, 35.074931278112359 ], [ -80.84044371799996, 35.07451947811235 ], [ -80.84086151799994, 35.074679878112342 ] ] } }, +{ "type": "Feature", "id": "fp-107", "properties": { "length_km": 2.7533002259573731, "slope_percent": 0.00116932, "main_id": 8, "toid": "fp-108", "realized_catchment": "cat-107" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.742801184999962, 35.149570811111538 ], [ -80.743302184999948, 35.148951878111554 ], [ -80.744110317999969, 35.148630411111569 ], [ -80.744891117999941, 35.148583878111587 ], [ -80.74611758499999, 35.148330811111485 ], [ -80.747623784999973, 35.148627011111614 ], [ -80.749799317999987, 35.148830878111546 ], [ -80.750898917999962, 35.148899078111555 ], [ -80.75260078499997, 35.148305278111494 ], [ -80.759912184999962, 35.144395878111595 ], [ -80.760219384999971, 35.144098478111502 ], [ -80.761112584999978, 35.143458011111655 ], [ -80.76125258499998, 35.14302301111163 ], [ -80.761811184999942, 35.142359278111634 ], [ -80.763988384999948, 35.140689278111623 ], [ -80.767366117999984, 35.13780641111164 ] ] } }, +{ "type": "Feature", "id": "fp-108", "properties": { "length_km": 3.3357380462778408, "slope_percent": 0.00116932, "main_id": 8, "toid": "fp-81", "realized_catchment": "cat-108" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.767366117999984, 35.13780641111164 ], [ -80.767923984999982, 35.137532078111654 ], [ -80.770547584999989, 35.135427211111704 ], [ -80.772585517999971, 35.133344678111726 ], [ -80.77339518499997, 35.132360478111778 ], [ -80.773423184999956, 35.1322230781117 ], [ -80.774176784999952, 35.131605278111728 ], [ -80.774623117999965, 35.131353678111708 ], [ -80.775822518, 35.130965411111767 ], [ -80.777105184999982, 35.130806011111751 ], [ -80.777328317999945, 35.13071461111177 ], [ -80.779225717999964, 35.129364811111707 ], [ -80.781708317999971, 35.128152878111784 ], [ -80.782182585, 35.127832478111777 ], [ -80.782796584999971, 35.127260411111749 ], [ -80.783132184999943, 35.126298678111795 ], [ -80.783579184999937, 35.125474478111791 ], [ -80.783830385, 35.125153878111753 ], [ -80.783886317999986, 35.125016611111825 ], [ -80.784750784999972, 35.124719478111764 ], [ -80.785559317999969, 35.124720078111828 ], [ -80.785977184999979, 35.124972278111784 ], [ -80.786004784999975, 35.125155611111836 ], [ -80.78675691799998, 35.125751611111788 ], [ -80.787230917999977, 35.125660411111831 ], [ -80.787816717999988, 35.12534007811179 ], [ -80.788904117999948, 35.125157678111783 ], [ -80.789322717999937, 35.124814411111828 ], [ -80.790634717999978, 35.123051811111758 ], [ -80.79150091799994, 35.121082611111859 ], [ -80.792310384999951, 35.119960811111866 ] ] } }, +{ "type": "Feature", "id": "fp-109", "properties": { "length_km": 3.2549439129884199, "slope_percent": 0.0050411, "main_id": 8, "toid": "fp-110", "realized_catchment": "cat-109" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.704422918999967, 35.199989278111019 ], [ -80.704561185, 35.198638011111065 ], [ -80.704449184999987, 35.198248611111083 ], [ -80.704086184999952, 35.197928278110972 ], [ -80.703974318999954, 35.197699278111045 ], [ -80.70394618499995, 35.197378678111058 ], [ -80.704140584999962, 35.196485278111076 ], [ -80.70402871899995, 35.196187611111064 ], [ -80.70402818499997, 35.195637878111064 ], [ -80.704445584999959, 35.19440087811099 ], [ -80.705642918999956, 35.191926611111128 ], [ -80.706255784999939, 35.191032878110981 ], [ -80.708038384999966, 35.187985611111088 ], [ -80.708707317999938, 35.187298011111089 ], [ -80.709181384999965, 35.187137411111074 ], [ -80.70957178499998, 35.186908078111117 ], [ -80.709794384999952, 35.186472678111166 ], [ -80.710100784999966, 35.185968678111173 ], [ -80.710881517999951, 35.185601678111119 ], [ -80.712414717999934, 35.184432611111191 ], [ -80.712637517999951, 35.184134611111183 ], [ -80.712692917999959, 35.183745211111209 ], [ -80.713334384999939, 35.183607278111175 ], [ -80.713752717999967, 35.183378011111074 ], [ -80.714701117999923, 35.183308611111215 ], [ -80.715481717999978, 35.182918678111214 ], [ -80.715843784999947, 35.182300011111167 ], [ -80.71589878499999, 35.181635811111185 ], [ -80.716065384999979, 35.180879878111206 ], [ -80.716092384999968, 35.18005527811119 ], [ -80.716314917999981, 35.179528278111214 ], [ -80.717764917999943, 35.178863078111256 ], [ -80.718127117999984, 35.178610811111241 ], [ -80.718349784999987, 35.178221278111224 ], [ -80.718488384999944, 35.177511211111259 ], [ -80.718989317999942, 35.176319878111173 ], [ -80.719184317999975, 35.176113478111198 ] ] } }, +{ "type": "Feature", "id": "fp-110", "properties": { "length_km": 2.5831911049452247, "slope_percent": 0.0050411, "main_id": 8, "toid": "fp-28", "realized_catchment": "cat-110" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.719184317999975, 35.176113478111198 ], [ -80.720940717999952, 35.175264811111248 ], [ -80.721637384999951, 35.174691611111164 ], [ -80.721999917999952, 35.174599811111271 ], [ -80.724259317999966, 35.174575078111253 ], [ -80.724844517999941, 35.174093611111253 ], [ -80.725290117999975, 35.17354347811127 ], [ -80.725568517999974, 35.173039411111205 ], [ -80.725484517999973, 35.172741678111322 ], [ -80.724953917999983, 35.172238211111193 ], [ -80.724673917999951, 35.171414011111267 ], [ -80.72414331799996, 35.170910478111367 ], [ -80.724143117999972, 35.170704411111387 ], [ -80.724532518, 35.169765011111302 ], [ -80.724866584999972, 35.169283678111292 ], [ -80.725897317999966, 35.168183611111353 ], [ -80.726621384999987, 35.167335478111319 ], [ -80.727485117999947, 35.166579011111352 ], [ -80.728544584999952, 35.166303211111376 ], [ -80.730410984999935, 35.164515211111379 ], [ -80.730939984999964, 35.163736078111327 ], [ -80.731608517999987, 35.163071278111332 ], [ -80.732361117999986, 35.162681211111462 ], [ -80.732973517999937, 35.161856078111391 ], [ -80.734980317999941, 35.16086947811133 ], [ -80.73545418499998, 35.160823278111401 ] ] } }, +{ "type": "Feature", "id": "fp-111", "properties": { "length_km": 0.37654076706372985, "slope_percent": 0.00234899, "main_id": 12, "toid": "fp-112", "realized_catchment": "cat-111" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.857170384999961, 35.15131961111156 ], [ -80.857532984999978, 35.150769878111525 ], [ -80.858063184999935, 35.149120878111638 ], [ -80.858119117999948, 35.148525478111566 ], [ -80.857923984999957, 35.148113211111557 ] ] } }, +{ "type": "Feature", "id": "fp-112", "properties": { "length_km": 1.2627087785939501, "slope_percent": 0.00234899, "main_id": 12, "toid": "fp-83", "realized_catchment": "cat-112" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.857923984999957, 35.148113211111557 ], [ -80.857812584999976, 35.147563411111555 ], [ -80.857952184999988, 35.147128278111623 ], [ -80.859067717999949, 35.146624611111605 ], [ -80.859234984999944, 35.146235211111609 ], [ -80.859625784999949, 35.144219678111639 ], [ -80.860183584999945, 35.143212078111624 ], [ -80.860323317999985, 35.141700411111607 ], [ -80.860797584999929, 35.140303278111695 ], [ -80.861522784999977, 35.139272678111688 ], [ -80.862191984999981, 35.138906278111655 ], [ -80.862693917999962, 35.138379478111652 ], [ -80.862944917999982, 35.138288011111648 ] ] } }, +{ "type": "Feature", "id": "fp-113", "properties": { "length_km": 4.6383467119891648, "slope_percent": 0.00223621, "main_id": 58, "toid": "fp-114", "realized_catchment": "cat-113" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.774049917999946, 35.233663611110572 ], [ -80.774747984999976, 35.23357261111061 ], [ -80.775502117999963, 35.233206678110605 ], [ -80.775753584999961, 35.233000811110671 ], [ -80.776312117999964, 35.23279507811057 ], [ -80.777931184999943, 35.232819278110718 ], [ -80.781198717999985, 35.231424678110564 ], [ -80.781785384999978, 35.230944278110606 ], [ -80.782512717999964, 35.229685011110647 ], [ -80.782596517999949, 35.229662278110645 ], [ -80.782652584999937, 35.229387411110658 ], [ -80.782960184999979, 35.228883678110606 ], [ -80.783826384999941, 35.228082878110683 ], [ -80.78502711799996, 35.227625678110712 ], [ -80.788321117999942, 35.227261611110642 ], [ -80.788795784999934, 35.227032878110641 ], [ -80.789243117999931, 35.226483478110666 ], [ -80.789717917999951, 35.22614021111076 ], [ -80.790694984999959, 35.226003478110663 ], [ -80.791253384999948, 35.225820678110708 ], [ -80.791979917999981, 35.225111211110672 ], [ -80.792259584999968, 35.224538811110733 ], [ -80.79228771799994, 35.224286878110725 ], [ -80.793099184999974, 35.222249078110735 ], [ -80.793742184999985, 35.221196011110827 ], [ -80.794720784999981, 35.219455811110755 ], [ -80.795782717999941, 35.218151011110713 ], [ -80.79650938499999, 35.216960478110849 ], [ -80.796956917999964, 35.215953078110765 ], [ -80.797907117999955, 35.214648211110848 ], [ -80.799024317999965, 35.213641078110811 ], [ -80.800141317999987, 35.212977678110889 ], [ -80.800532584999985, 35.212382411110902 ], [ -80.800589517999981, 35.211099878110872 ], [ -80.801511117999951, 35.210436078110867 ], [ -80.801678784999979, 35.210161411110874 ], [ -80.802488584999935, 35.209566411110877 ], [ -80.802489517999959, 35.208512811110865 ], [ -80.802657184999987, 35.208283878110912 ], [ -80.804779184999973, 35.206979678110912 ], [ -80.80653798499999, 35.206110411110849 ], [ -80.806727366786774, 35.206036181330866 ] ] } }, +{ "type": "Feature", "id": "fp-114", "properties": { "length_km": 4.6383467119890183, "slope_percent": 0.00223621, "main_id": 58, "toid": "fp-115", "realized_catchment": "cat-114" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.806727366786774, 35.206036181330866 ], [ -80.808463984999932, 35.205355478110896 ], [ -80.809468984999967, 35.204875078110945 ], [ -80.809943717999943, 35.204486011110902 ], [ -80.810921184999984, 35.203341411110955 ], [ -80.812458317999983, 35.200364678110937 ], [ -80.812514517999944, 35.199838011110934 ], [ -80.812431518, 35.198967611110994 ], [ -80.812208917999953, 35.198234478111019 ], [ -80.812209584999948, 35.19731841111102 ], [ -80.812321984999983, 35.196356478111014 ], [ -80.813048984999966, 35.194272611111096 ], [ -80.813412717999952, 35.192875611111077 ], [ -80.813999784999965, 35.191341478111084 ], [ -80.815872317999947, 35.187128078111144 ], [ -80.816430784999966, 35.186235078111153 ], [ -80.816933517999985, 35.185617011111141 ], [ -80.817687184999954, 35.184930278111146 ], [ -80.818245584999957, 35.184289211111185 ], [ -80.818915518, 35.183762811111201 ], [ -80.820561917999953, 35.183076278111102 ], [ -80.822012917999984, 35.182687611111163 ], [ -80.824747384999966, 35.181635211111164 ], [ -80.828849117999951, 35.180285478111273 ], [ -80.83021658499996, 35.179278211111232 ], [ -80.831025717999978, 35.178957878111184 ], [ -80.831722984999942, 35.178866478111239 ], [ -80.832476317999976, 35.178614811111174 ], [ -80.832811184999969, 35.178385878111293 ], [ -80.83295078499998, 35.17813401111119 ], [ -80.832867317999956, 35.1779050111112 ], [ -80.832253917999935, 35.177378011111294 ], [ -80.831334184999946, 35.175797278111233 ], [ -80.831306584999936, 35.175453678111253 ] ] } }, +{ "type": "Feature", "id": "fp-115", "properties": { "length_km": 4.0128900767501037, "slope_percent": 0.00223621, "main_id": 58, "toid": "fp-111", "realized_catchment": "cat-115" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.831306584999936, 35.175453678111253 ], [ -80.83147411799996, 35.175133211111287 ], [ -80.833371584999952, 35.173645011111262 ], [ -80.834264784999959, 35.172729211111232 ], [ -80.834543784999937, 35.172591878111263 ], [ -80.835241184999973, 35.172500478111253 ], [ -80.837612184999955, 35.172432478111318 ], [ -80.838142317999939, 35.172226611111263 ], [ -80.837641117999965, 35.170279478111304 ], [ -80.837641584999972, 35.169248878111347 ], [ -80.837976784999981, 35.168172478111387 ], [ -80.838004917999967, 35.167508278111377 ], [ -80.838284518, 35.166294478111347 ], [ -80.838702984999941, 35.165836611111366 ], [ -80.839093717999958, 35.165264078111377 ], [ -80.83959598499996, 35.164760278111395 ], [ -80.840460717999974, 35.16446281111137 ], [ -80.841018584999972, 35.164371411111397 ], [ -80.841994717999981, 35.164554878111275 ], [ -80.842134317999964, 35.164302878111371 ], [ -80.842525184999943, 35.16320367811138 ], [ -80.842999584999987, 35.162379278111374 ], [ -80.843585517999927, 35.162012878111376 ], [ -80.84422731799998, 35.161394678111442 ], [ -80.844757184999949, 35.161005478111413 ], [ -80.845370917999958, 35.160776611111338 ], [ -80.846682118, 35.160021011111411 ], [ -80.846988984999939, 35.15949441111146 ], [ -80.847658584999962, 35.158555411111514 ], [ -80.848049117999963, 35.158395211111404 ], [ -80.84916471799994, 35.158280878111448 ], [ -80.84961098499997, 35.158166478111404 ], [ -80.849945784999989, 35.157868811111499 ], [ -80.850810517999946, 35.157433811111524 ], [ -80.851172984999948, 35.157342278111443 ] ] } }, +{ "type": "Feature", "id": "fp-116", "properties": { "length_km": 1.0366153628001999, "slope_percent": 0.00270819, "main_id": 58, "toid": "fp-117", "realized_catchment": "cat-116" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.765662117999966, 35.243779611110469 ], [ -80.766304717999958, 35.243413811110493 ], [ -80.76717098499995, 35.24268161111052 ], [ -80.767897518, 35.242201278110528 ], [ -80.76840031799999, 35.241972678110464 ], [ -80.769126784999969, 35.241469478110503 ], [ -80.769434517999969, 35.241011678110567 ], [ -80.769462984999961, 35.240507878110613 ], [ -80.769073184999968, 35.239728811110567 ], [ -80.76912978499999, 35.239156278110563 ], [ -80.769353717999934, 35.238721278110575 ], [ -80.769410717999961, 35.237874011110577 ], [ -80.770137184999967, 35.237301878110593 ], [ -80.770640917999955, 35.236340411110589 ] ] } }, +{ "type": "Feature", "id": "fp-117", "properties": { "length_km": 0.4407276451358812, "slope_percent": 0.00270819, "main_id": 58, "toid": "fp-113", "realized_catchment": "cat-117" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.770640917999955, 35.236340411110589 ], [ -80.772681384999942, 35.23425787811059 ], [ -80.773016584999937, 35.234029211110567 ], [ -80.773938184999963, 35.233778011110644 ], [ -80.774049917999946, 35.233663611110572 ] ] } }, +{ "type": "Feature", "id": "fp-118", "properties": { "length_km": 0.19480637944887677, "slope_percent": 0.00823416, "main_id": 50, "toid": "fp-119", "realized_catchment": "cat-118" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.869238717999963, 35.230109011110692 ], [ -80.869238784999951, 35.228368278110622 ] ] } }, +{ "type": "Feature", "id": "fp-119", "properties": { "length_km": 0.32654560158194201, "slope_percent": 0.00823416, "main_id": 50, "toid": "fp-94", "realized_catchment": "cat-119" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.869238784999951, 35.228368278110622 ], [ -80.869322584999964, 35.2281622111106 ], [ -80.869322717999978, 35.22697121111063 ], [ -80.869238984999981, 35.226650478110699 ], [ -80.869211184999983, 35.225596878110665 ], [ -80.869099518, 35.225505278110731 ] ] } }, +{ "type": "Feature", "id": "fp-120", "properties": { "length_km": 0.85118579756963542, "slope_percent": 0.00193071, "main_id": 50, "toid": "fp-121", "realized_catchment": "cat-120" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.869795717999978, 35.247424011110439 ], [ -80.869404917999972, 35.246278678110428 ], [ -80.869377117999989, 35.244423611110484 ], [ -80.869488784999987, 35.243461611110412 ], [ -80.869405117999975, 35.2428202781105 ], [ -80.869181784999967, 35.242339411110507 ], [ -80.868539784999953, 35.241812611110511 ], [ -80.868260584999987, 35.241446211110429 ], [ -80.868372317999956, 35.240782011110554 ], [ -80.868288584999959, 35.240186478110573 ] ] } }, +{ "type": "Feature", "id": "fp-121", "properties": { "length_km": 1.3138266734564177, "slope_percent": 0.00193071, "main_id": 50, "toid": "fp-118", "realized_catchment": "cat-121" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.868288584999959, 35.240186478110573 ], [ -80.867925784999969, 35.239224478110543 ], [ -80.867144317999987, 35.237735811110618 ], [ -80.866558317999974, 35.236773811110453 ], [ -80.866418784999951, 35.236247011110592 ], [ -80.866195384999969, 35.235811811110672 ], [ -80.86622351799997, 35.235330811110501 ], [ -80.86775878499999, 35.234918678110539 ], [ -80.868456784999978, 35.234598078110572 ], [ -80.868791784999971, 35.233956678110644 ], [ -80.868791917999943, 35.231895411110578 ], [ -80.869099117999951, 35.230246411110599 ], [ -80.869238717999963, 35.230109011110692 ] ] } }, +{ "type": "Feature", "id": "fp-122", "properties": { "length_km": 2.8100881323871283, "slope_percent": 0.00444198, "main_id": 42, "toid": "fp-123", "realized_catchment": "cat-122" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.674029584999985, 35.187225011111032 ], [ -80.675088918999961, 35.185781611111189 ], [ -80.675311385, 35.184659211111139 ], [ -80.675645584999984, 35.183651278111171 ], [ -80.675672918999965, 35.182735211111186 ], [ -80.675895518999937, 35.181864811111247 ], [ -80.676229984999964, 35.181452278111216 ], [ -80.676508584999979, 35.180879678111133 ], [ -80.676843318999943, 35.180627611111248 ], [ -80.677010518999964, 35.180329811111164 ], [ -80.676954518999935, 35.180009211111205 ], [ -80.676703184999951, 35.179619878111239 ], [ -80.676395918999958, 35.178841278111271 ], [ -80.676450584999941, 35.177192211111304 ], [ -80.677815918999968, 35.174672278111217 ], [ -80.678651984999988, 35.173572611111275 ], [ -80.679069984999956, 35.172770811111285 ], [ -80.680295918999946, 35.17082347811121 ], [ -80.681383118999975, 35.170044278111348 ], [ -80.688073086483058, 35.167107419470824 ] ] } }, +{ "type": "Feature", "id": "fp-123", "properties": { "length_km": 2.8100881323871194, "slope_percent": 0.00444198, "main_id": 42, "toid": "fp-124", "realized_catchment": "cat-123" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.688073086483058, 35.167107419470824 ], [ -80.688799984999946, 35.166788278111348 ], [ -80.689078718999951, 35.166513411111303 ], [ -80.689859584999965, 35.166238078111334 ], [ -80.690919384999972, 35.166214678111359 ], [ -80.693289184999955, 35.165045278111272 ], [ -80.694265318999953, 35.164930211111376 ], [ -80.696969118999959, 35.163188011111359 ], [ -80.697414984999966, 35.162706811111399 ], [ -80.697693184999935, 35.161973678111352 ], [ -80.697720718999975, 35.161561411111371 ], [ -80.697943584999962, 35.161309411111397 ], [ -80.698250184999949, 35.161125878111399 ], [ -80.698752118999963, 35.160988211111466 ], [ -80.699895784999953, 35.161239478111405 ], [ -80.700900384999954, 35.161857278111384 ], [ -80.701709384999958, 35.162062878111399 ], [ -80.702183384999955, 35.161879411111421 ], [ -80.704106118999974, 35.160160411111363 ], [ -80.705388518999939, 35.159609878111468 ], [ -80.708928517999937, 35.157752211111443 ], [ -80.710573384999975, 35.157224278111478 ], [ -80.711186985, 35.157292611111437 ], [ -80.711605384999984, 35.157429811111413 ], [ -80.711940384999949, 35.157704278111417 ], [ -80.712191718, 35.15804781111148 ], [ -80.712526717999935, 35.158299411111493 ], [ -80.71308451799996, 35.158390611111336 ] ] } }, +{ "type": "Feature", "id": "fp-124", "properties": { "length_km": 2.5243915790754512, "slope_percent": 0.00444198, "main_id": 42, "toid": "fp-35", "realized_catchment": "cat-124" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.71308451799996, 35.158390611111336 ], [ -80.714116384999954, 35.158367011111373 ], [ -80.714562117999947, 35.15793161111138 ], [ -80.715621517999978, 35.15756421111147 ], [ -80.715872117999965, 35.157289278111435 ], [ -80.716680384999961, 35.156922211111478 ], [ -80.71743218499995, 35.155822278111472 ], [ -80.718128784999976, 35.155295011111491 ], [ -80.718685917999963, 35.154767811111498 ], [ -80.719801184999938, 35.154537878111448 ], [ -80.720052117999941, 35.154423211111485 ], [ -80.720692784999983, 35.153873011111493 ], [ -80.720692517999964, 35.153529411111485 ], [ -80.720608784999968, 35.153483678111527 ], [ -80.720579984999986, 35.152750811111495 ], [ -80.720774317999954, 35.151971878111446 ], [ -80.721192384999938, 35.151696678111534 ], [ -80.721833717999985, 35.151650411111483 ], [ -80.722753584999964, 35.151397811111487 ], [ -80.723423517999947, 35.151924011111461 ], [ -80.723591184999975, 35.152267411111502 ], [ -80.723758584999985, 35.152358878111521 ], [ -80.724372184999936, 35.152381278111477 ], [ -80.72501278499999, 35.151831078111435 ], [ -80.725430784999958, 35.151624611111494 ], [ -80.725792984999941, 35.15132661111145 ], [ -80.725652784999966, 35.150731278111508 ], [ -80.725317717999985, 35.150273411111485 ], [ -80.725317317999952, 35.149952811111518 ], [ -80.725456317999956, 35.14956327811155 ], [ -80.725706717999969, 35.149196678111558 ], [ -80.726431584999943, 35.149012811111568 ], [ -80.727491117999989, 35.148897411111555 ], [ -80.728494317999932, 35.148323878111533 ], [ -80.729358384999955, 35.148094211111562 ], [ -80.729859984999962, 35.147819011111579 ], [ -80.730444784999989, 35.147245878111484 ], [ -80.730974384999953, 35.147016411111622 ], [ -80.731029784999976, 35.146764411111548 ] ] } }, +{ "type": "Feature", "id": "fp-125", "properties": { "length_km": 4.9265439291819249, "slope_percent": 0.0034316, "main_id": 54, "toid": "fp-126", "realized_catchment": "cat-125" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.78707131799996, 35.170185211111281 ], [ -80.787713717999964, 35.169406811111251 ], [ -80.78888651799997, 35.16819381111133 ], [ -80.789388917999986, 35.167804678111324 ], [ -80.790812917999972, 35.166408611111351 ], [ -80.794022784999981, 35.164074611111332 ], [ -80.795753717999972, 35.162358011111372 ], [ -80.796367984999961, 35.161556678111396 ], [ -80.79798711799998, 35.160046078111435 ], [ -80.798489184999951, 35.159817411111398 ], [ -80.799074717999972, 35.160092611111395 ], [ -80.799604717999983, 35.160001411111409 ], [ -80.799855917999935, 35.159772478111414 ], [ -80.800135584999978, 35.158925211111395 ], [ -80.800498784999945, 35.15814661111142 ], [ -80.801531184999988, 35.157574811111395 ], [ -80.802256517999965, 35.157391878111447 ], [ -80.802535584999987, 35.157231678111494 ], [ -80.804069784999967, 35.15679747811145 ], [ -80.805129984999951, 35.156362878111381 ], [ -80.805631984999934, 35.156317411111424 ], [ -80.806886718, 35.156501278111477 ], [ -80.807165717999965, 35.156409811111452 ], [ -80.807891317999974, 35.155883478111413 ], [ -80.808365717999948, 35.15544861111146 ], [ -80.80858918499996, 35.154967678111461 ], [ -80.809286717999953, 35.154532878111453 ], [ -80.810012517999951, 35.153662878111454 ], [ -80.811128384999961, 35.15322827811147 ], [ -80.811351784999957, 35.152839078111519 ], [ -80.811575784999945, 35.151694011111495 ], [ -80.812106317999962, 35.15082387811151 ], [ -80.812190184999977, 35.150503278111501 ], [ -80.811800317999939, 35.149839011111482 ], [ -80.811968384999943, 35.148716811111541 ], [ -80.812750384999958, 35.147205478111538 ], [ -80.812778517999959, 35.147022211111477 ], [ -80.813029585, 35.146793411111581 ], [ -80.81355958499995, 35.14663341111153 ], [ -80.815009717999942, 35.146382078111458 ], [ -80.816042117999984, 35.145558078111641 ], [ -80.816684184999986, 35.144458878111607 ], [ -80.817381717999979, 35.143863811111608 ], [ -80.817744584999957, 35.143337211111607 ], [ -80.818023517999961, 35.143222811111578 ], [ -80.81874851799995, 35.143154411111631 ], [ -80.820142917999988, 35.142857278111599 ], [ -80.820310384999985, 35.142697078111645 ], [ -80.820310517999985, 35.142376411111563 ], [ -80.820004117999986, 35.1419410781116 ], [ -80.819892917999937, 35.141528678111676 ], [ -80.819837717999974, 35.140750011111649 ] ] } }, +{ "type": "Feature", "id": "fp-126", "properties": { "length_km": 4.8765959156900847, "slope_percent": 0.0034316, "main_id": 54, "toid": "fp-88", "realized_catchment": "cat-126" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.819837717999974, 35.140750011111649 ], [ -80.820534984999938, 35.140383878111749 ], [ -80.820535317999941, 35.139880011111671 ], [ -80.820423917999975, 35.13969667811164 ], [ -80.820396517999924, 35.138940878111711 ], [ -80.820201784999952, 35.138345278111728 ], [ -80.820202117999955, 35.137795611111727 ], [ -80.820704184999954, 35.137521011111637 ], [ -80.821150917999958, 35.1365134111117 ], [ -80.82117898499996, 35.136169878111708 ], [ -80.820733317999952, 35.135757411111683 ], [ -80.820705717999942, 35.135253478111757 ], [ -80.82090098499998, 35.13502461111166 ], [ -80.82148658499996, 35.134979011111689 ], [ -80.822239117999956, 35.135231278111661 ], [ -80.822685184999955, 35.135231411111619 ], [ -80.823019917999929, 35.135117078111726 ], [ -80.823605717999939, 35.13475081111168 ], [ -80.823606117999987, 35.133995011111729 ], [ -80.823522584999978, 35.133949211111712 ], [ -80.823411184999941, 35.133605478111718 ], [ -80.823440184999981, 35.132002278111742 ], [ -80.823105717999965, 35.131750211111729 ], [ -80.822380917999965, 35.131566678111732 ], [ -80.821182584999974, 35.130993678111771 ], [ -80.820848117999958, 35.130695678111756 ], [ -80.82084838499999, 35.130306278111703 ], [ -80.820960185, 35.129939878111813 ], [ -80.822076317999972, 35.128474611111777 ], [ -80.823331584999949, 35.127284078111749 ], [ -80.824530784999979, 35.126620278111766 ], [ -80.825451184999963, 35.125887811111781 ], [ -80.826929184999983, 35.1251554781118 ], [ -80.82754298499999, 35.124170878111812 ], [ -80.827543184999968, 35.123827278111882 ], [ -80.827431917999959, 35.123644011111715 ], [ -80.827264984999942, 35.122933878111887 ], [ -80.82718138499996, 35.122865211111872 ], [ -80.827181717999963, 35.122246811111864 ], [ -80.828744784999969, 35.118972211111853 ], [ -80.828744984999958, 35.118422478111938 ], [ -80.828661384999975, 35.118353678111895 ], [ -80.828578517999972, 35.117162678111903 ], [ -80.828941184999962, 35.116727611111841 ], [ -80.829805317999956, 35.116498878111877 ], [ -80.830112117999988, 35.116338678111909 ], [ -80.830446784999936, 35.115949411111949 ], [ -80.83106111799998, 35.114048611111933 ], [ -80.831229518, 35.111941611111973 ], [ -80.83148078499994, 35.111277411111956 ], [ -80.832010717999935, 35.110521811112008 ], [ -80.832373184999923, 35.11031581111191 ], [ -80.834157185, 35.109904211111974 ], [ -80.83666578499998, 35.109813278112014 ], [ -80.83803151799998, 35.109951078111997 ], [ -80.838756184999966, 35.109974211111982 ] ] } }, +{ "type": "Feature", "id": "fp-127", "properties": { "length_km": 2.7030788400847245, "slope_percent": 0.00611449, "main_id": 93, "toid": "fp-128", "realized_catchment": "cat-127" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.863885984999982, 35.185378011111162 ], [ -80.863356117999942, 35.184874078111129 ], [ -80.862351784999987, 35.184461811111156 ], [ -80.862156517999949, 35.184255611111304 ], [ -80.862100784999939, 35.184026611111072 ], [ -80.86198918499997, 35.183934878111152 ], [ -80.861961317999942, 35.183728811111187 ], [ -80.861849717999974, 35.183614278111101 ], [ -80.861849784999947, 35.183454011111131 ], [ -80.861765984999977, 35.183408211111221 ], [ -80.861766117999935, 35.182766878111146 ], [ -80.86165458499994, 35.182492011111187 ], [ -80.859841784999958, 35.180865611111166 ], [ -80.859702317999961, 35.180636611111154 ], [ -80.85953511799994, 35.179789211111206 ], [ -80.859451384999943, 35.179743411111232 ], [ -80.859395717999973, 35.179560078111145 ], [ -80.859005184999944, 35.17921647811125 ], [ -80.857833717999938, 35.178437611111221 ], [ -80.857750117999956, 35.178254411111219 ], [ -80.85769451799996, 35.177292411111203 ], [ -80.857331984999973, 35.176582411111212 ], [ -80.857025317999955, 35.176238678111275 ], [ -80.856969517999971, 35.175963878111162 ], [ -80.856913718, 35.175918078111266 ], [ -80.856300517999955, 35.173742211111232 ], [ -80.855659184999965, 35.17266561111132 ], [ -80.855603717999941, 35.171726478111296 ], [ -80.855715517999954, 35.170650011111313 ], [ -80.855632184999934, 35.16900101111132 ], [ -80.854823917999951, 35.166962411111292 ], [ -80.854544984999961, 35.166687611111328 ], [ -80.853568917999951, 35.166183611111336 ], [ -80.853262317999949, 35.165931478111318 ], [ -80.85312291799994, 35.165656678111304 ], [ -80.853123184999959, 35.164351211111359 ] ] } }, +{ "type": "Feature", "id": "fp-128", "properties": { "length_km": 1.778775079356449, "slope_percent": 0.00611449, "main_id": 93, "toid": "fp-111", "realized_catchment": "cat-128" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.853123184999959, 35.164351211111359 ], [ -80.853681184999971, 35.163778678111441 ], [ -80.853709184999957, 35.163229011111426 ], [ -80.854825184999982, 35.16190067811138 ], [ -80.854880984999966, 35.161625878111451 ], [ -80.854379384999959, 35.160434811111394 ], [ -80.85365438499997, 35.160045411111376 ], [ -80.853403384999979, 35.159770478111476 ], [ -80.853403717999981, 35.158785678111499 ], [ -80.853069184999967, 35.158167211111412 ], [ -80.852595184999984, 35.157777811111423 ], [ -80.852567517999944, 35.157365478111473 ], [ -80.852818584999966, 35.156861611111438 ], [ -80.852818717999966, 35.156518078111532 ], [ -80.85248411799995, 35.156220278111391 ], [ -80.85251198499999, 35.156105811111424 ] ] } }, +{ "type": "Feature", "id": "fp-129", "properties": { "length_km": 0.90902951712589586, "slope_percent": 0.00019063, "main_id": 1, "toid": "fp-130", "realized_catchment": "cat-129" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.899350384999948, 35.011741011113102 ], [ -80.899879184999975, 35.011122478113023 ], [ -80.900491384999967, 35.010274878113073 ], [ -80.900491317999979, 35.009931411113044 ], [ -80.900351985, 35.009702411113068 ], [ -80.899878584999968, 35.00929021111309 ], [ -80.899599984999938, 35.00883221111313 ], [ -80.899516317999939, 35.008030478113135 ], [ -80.899683184999958, 35.007755678113114 ], [ -80.899989384999969, 35.007572411113166 ], [ -80.900462717999957, 35.007526478113157 ], [ -80.900908184999935, 35.007595011113175 ], [ -80.901520517999984, 35.007549078113051 ], [ -80.901965917999973, 35.007297011113074 ], [ -80.902271984999985, 35.006999211113154 ], [ -80.902522184999938, 35.006266211113129 ], [ -80.90232718499999, 35.00583101111306 ] ] } }, +{ "type": "Feature", "id": "fp-130", "properties": { "length_km": 0.92720327574750683, "slope_percent": 0.00019063, "main_id": 1, "toid": "fp-0", "realized_catchment": "cat-130" }, "geometry": { "type": "LineString", "coordinates": [ [ -80.90232718499999, 35.00583101111306 ], [ -80.901909517999968, 35.005441811113187 ], [ -80.897732717999986, 35.002281878113187 ], [ -80.896674784999973, 35.001824011113165 ], [ -80.894948584999952, 35.000156878113252 ] ] } } +] +} From 6faf3e37f8633dea55f0eac9dc871f386738b2db Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Mon, 30 Nov 2020 14:23:16 -0500 Subject: [PATCH 04/54] network merging utility functions --- .../troute/nhd_network_augment.py | 509 ++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 src/python_framework_v02/troute/nhd_network_augment.py diff --git a/src/python_framework_v02/troute/nhd_network_augment.py b/src/python_framework_v02/troute/nhd_network_augment.py new file mode 100644 index 000000000..812088b02 --- /dev/null +++ b/src/python_framework_v02/troute/nhd_network_augment.py @@ -0,0 +1,509 @@ +import nhd_network +import numpy as np +from functools import partial + + +def network_connections(data, network_data): + + """ + Extract upstream and downstream connections between segments in network + Args: + data (DataFrame): Network parameter dataset, prepared + network_data (dict): network metadata + Returns: + conn (dict): downstream connections + rconn (dict): upstream connections + """ + + # extract downstream connections + conn = nhd_network.extract_connections(data, network_data["downstream_col"]) + + # extract upstream connections + rconn = nhd_network.reverse_network(conn) + + return conn, rconn + +def build_reaches(rconn): + + # isolate independent subnetworks + subnets = nhd_network.reachable_network(rconn) + + # identify the segments in each subnetwork + subreachable = nhd_network.reachable(rconn) + + # break each subnetwork into reaches + subreaches = {} + for tw, net in subnets.items(): + path_func = partial(nhd_network.split_at_junction, net) + subreaches[tw] = nhd_network.dfs_decomposition(net, path_func) + + return subreachable, subreaches, subnets + +def headwater_connections(data, network_data): + + """ + Determine which segments are and are not headwaters. + Headwaters are defined as segments with no upstream connection, only downstream connections. + Non-headwaters are defined as segments with both upstream and downstream connections. + Args: + data (DataFrame): Network parameter dataset, prepared + Returns: + hw_conn (dict): downstream connections, headwaters only + non_hw_conn (dict): downstream connections, non headwaters only + """ + + # extract network connections + conn, rconn = network_connections(data, network_data) + + hw = [] # store headwater segments + non_hw = [] # store non-headwater segments + + for seg in rconn.keys(): + # if there is no upstream connection anda downstream connection, it is a headwater + if bool(rconn[seg]) == False and bool(conn[seg]) == True: + hw.append(seg) + + # if there is an upstream connection and a downstream connection, it is a non-headwater (midwater?) + elif bool(rconn[seg]) == True and bool(conn[seg]) == True: + non_hw.append(seg) + + # get segment key-value pairs from the connections dictionary + hw_conn = {key: conn[key] for key in hw} + non_hw_conn = {key: conn[key] for key in non_hw} + + return hw_conn, non_hw_conn + +def prune_headwaters(hw_connections, non_hw_connections, data, thresh): + + # TO DO: Bug fix - why does the function return non-unique headwater IDs in the chop list? + # some headwaters are being identified for chopping more than once? + + """ + Prune headwaters from the network + Headwaters are pruned if they are shorter than the threshold length + OR if they merge with a midwater that is less than the threshold length + Args: + hw_connections (dict): downstream connections, headwaters only + non_hw_connections (dict): downstream connections, midwaters only + data (DataFrame): Network to be pruned + thresh (int): length threshold, segments below this length are slated for pruning + Returns: + chop (list): list of pruned headwater indices, updated + data_pruned (DataFrame): pruned network + """ + + # initialize a list of pruned headwater segments + chop = [] + + # loop through keys and values in headwater connections dictionary + for hw_k, srch_v in hw_connections.items(): + + """ + Trim headwater-headwater junctions + + - Headwater-headwater junctions are junctions where two (or more) headwaters merge. + - Once headwater-headwater junctions are identified, the shortest headwater below the + threshold length is pruned. + """ + + # create a list of headwaters draining to the specified downstream connections, srch_v + G = [k for k, v in hw_connections.items() if v == srch_v] + + # if more than one headwater drains into the segment, it is a headwater-headwater junction + if len(G) > 1: + + # get segment lengths from the test dataset + hw_len = data.loc[G].Length + + # find the shortest segment + hw_min_len = hw_len[hw_len.idxmin()] + + # check to see if headwater shorter than threshold length + if hw_min_len < thresh: + + # update chopping block + chop.append(hw_len.idxmin()) + + ############################################################ + # Trim headwater-midwater junctions + ############################################################ + + """ + Trim headwater-midwater junctions + + - Headwater-midwater junctions are junctions a headwater merges with a non-headwater (i.e. midwater). + - If the headwater is shorter than the threshold, it is pruned. + - If the headwater is longer than the threshold, but joins with or connects to a midwater that is + shorter than the threshold, the headwater is pruned. + - This is done to minimize the number of small midwater segments stranded between headwaters. + Else, these short, stranded segments cannot be merged upstream or downstream. + """ + + # midwaters draining into a junction that a headwater does too + H = [k for k, v in non_hw_connections.items() if v == srch_v] + + # if there is a corresponding midwater, then the headwater is a candidate for trimming + if bool(H) == True: + + # get segment lengths from the dataset + hw_len = data.loc[hw_k].Length + + # get midwater (in) length from the test dataset + mw_in_len = data.loc[H[0]].Length + + # get midwater (out) length from the test dataset + mw_out_len = data.loc[srch_v[0]].Length + + # check to see if headwater or midwaters (in/out) are shorter than the threshold + if hw_len < thresh or mw_in_len < thresh or mw_out_len < thresh: + + # update chopping block + chop.append(hw_k) + + # chop segments from the network + data_pruned = data.drop(np.unique(chop)) + + return np.unique(chop), data_pruned + +def len_weighted_av(df, var, weight): + + """ + Calculate a weighted average + Args: + df (DataFrame): DataFrame containing variables to be averaged and used as weights + var (str): name of the variable to be averaged + weight (str): name of the variable to be used as a weight + Returns: + x (float32): weighted average + """ + + x = (df[weight] * df[var]).sum() / df[weight].sum() + + return x + +def merge_parameters(to_merge): + + """ + length-weighted averaging of channel routing parameters across merged segments + Args: + to_merge (DataFrame): DataFrame containing routing parameters for segments to be merged together + Returns: + replace (DataFrame): weighted average + """ + + data_replace = to_merge.tail(1) + + idx = to_merge.tail(1).index + data_replace.loc[idx, "Length"] = to_merge.Length.sum() + data_replace.loc[idx, "n"] = len_weighted_av(to_merge, "n", "Length") + data_replace.loc[idx, "nCC"] = len_weighted_av(to_merge, "nCC", "Length") + data_replace.loc[idx, "So"] = len_weighted_av(to_merge, "So", "Length") + data_replace.loc[idx, "BtmWdth"] = len_weighted_av(to_merge, "BtmWdth", "Length") + data_replace.loc[idx, "TopWdth"] = len_weighted_av(to_merge, "TopWdth", "Length") + data_replace.loc[idx, "TopWdthCC"] = len_weighted_av( + to_merge, "TopWdthCC", "Length" + ) + data_replace.loc[idx, "MusK"] = len_weighted_av(to_merge, "MusK", "Length") + data_replace.loc[idx, "MusX"] = len_weighted_av(to_merge, "MusX", "Length") + data_replace.loc[idx, "ChSlp"] = len_weighted_av(to_merge, "ChSlp", "Length") + + return data_replace + +def correct_reach_connections(data_merged): + + """ + Update downstream connections ("to") for segments in a merged reach. + Only updates *in-reach* connections. + Args: + data_merged (DataFrame): Routing parameters for segments in merged reach + Returns: + data_merged (DataFrame): Routing parameters for segments in merged reach with updated donwstream connections + """ + + for i, idx in enumerate(data_merged.index.values[0:-1]): + data_merged.loc[idx, "to"] = data_merged.index.values[i + 1] + + return data_merged + +def upstream_merge(data_merged, chop): + + """ + Merge a short reach tail segment with upstream neighbor + Args: + data_merged (DataFrame): Routing parameters for segments in merged reach + chop (list): list of merged-out segments + Returns: + data_merged (DataFrame): Routing parameters for segments in merged reach with updated donwstream connections + chop (list): updated list of merged-out segments + """ + + # grab the two segments that need to be merged - simply the last two segments of the reach + to_merge = data_merged.tail(2) + + # calculate new parameter values + data_replace = merge_parameters(to_merge) + + # paste new parameters in to data_merged + data_merged.loc[to_merge.tail(1).index] = data_replace + + # remove merged segments from data_merged + data_merged = data_merged.drop(to_merge.head(1).index) + + # update "chop" list with merged-out segment IDs + chop.append(to_merge.head(1).index.values[0]) + + return data_merged, chop + +def downstream_merge(data_merged, chop, thresh): + + """ + Merge short segments with their downstream neighbors + Args: + data_merged (DataFrame): Routing parameters for segments in merged reach + chop (list): list of merged-out segments + thresh (int): theshold reach length (meters) + Returns: + data_merged (DataFrame): Routing parameters for segments in merged reach with updated donwstream connections + chop (list): updated list of merged-out segments + """ + + # find the upstream-most short segment and it's downstream connection + idx_us = data_merged.loc[data_merged.Length < thresh].head(1).index.values[0] + + pos_idx_us = data_merged.index.get_loc(idx_us) + idx_to = data_merged.iloc[pos_idx_us + 1].name + + # grab segments to be merged + to_merge = data_merged.loc[[idx_us, idx_to]] + + # calculate new parameter values + data_replace = merge_parameters(to_merge) + + # paste new parameters in to data_merged + data_merged.loc[to_merge.tail(1).index] = data_replace + + # remove merged segments from data_merged + data_merged = data_merged.drop(to_merge.head(1).index) + + # update "chop" list with merged-out segment IDs + chop.append(to_merge.head(1).index.values[0]) + + return data_merged, chop + +def merge_all(rch, data, chop): + + """ + Merge all segments in a reach + Args: + rch (list): Segment indices in the reach to be merged + data (DataFrame): Routing parameters for network containing the reach to be merged + chop (list): list of merged-out segments + Returns: + data_merged (DataFrame): Routing parameters for segments in merged reach with updated donwstream connections + chop (list): updated list of merged-out segments + """ + + # subset the model parameter data for this reach + data_merged = data.loc[rch].copy() + + # grab the two segments that need to be merged - in this case, merge all segments! + to_merge = data_merged.copy() + + # calculate new parameter values + data_replace = merge_parameters(to_merge) + + # paste new parameters in to data_merged + data_merged.loc[to_merge.tail(1).index] = data_replace + + # remove merged segments from data_merged - in this case, all but the last + data_merged = data_merged.drop(data_merged.iloc[:-1, :].index) + + # update "chop" list with merged-out segment IDs + chop.extend(list(to_merge.iloc[:-1, :].index)) + + return data_merged, chop + +def update_network_data(data, rch, data_merged, chop, rconn): + + """ + Update the network routing parameter data with merged segment data + Args: + data (DataFrame): Routing parameters for network to be updated + rch (list): Segment indices in the reach to be merged + data_merged (DataFrame): Routing parameters for merged reach + Returns: + data (DataFrame): Updated network routing parameters + """ + + # make a copy of the data to be replaced with merged + data_old = data.loc[rch].copy() + + # drop the segments that disapeared with merger + data = data.drop(chop) + + # adjust the segment data for those that remain + data.loc[data_merged.index] = data_merged + + # update out of reach connections - these will change in the first segment was merged out + upstreams = rconn[ + data_old.head(1).index.values[0] + ] # upstream connection of the OLD reach head + + if bool(upstreams): + + data.loc[upstreams, "to"] = data_merged.head(1).index.values[ + 0 + ] # index of NEW reach head + + return data + +def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_segments, network_data): + + if bool(list(pruned_segments)): + + segments = merged_segments + list(pruned_segments) + + else: + + segments = merged_segments + + # compute connections + conn = nhd_network.extract_connections(data_native, network_data["downstream_col"]) + + # initialize a library to store qlat desinations for pruned/merged segments + qlat_destinations = {x: {} for x in segments} + + for idx in segments: + + # get the downstream connection of this segment in the original network + ds_idx = conn[idx] + + # find the nearest downstream segment remaining in the pruned/merged network + while bool(ds_idx[0] in data_merged.index) == False: + ds_idx = conn[ds_idx[0]] + + # update the qlat destination dict + qlat_destinations[idx] = ds_idx + + return qlat_destinations + +def segment_merge(data_native, data, network_data, thresh, pruned_segments): + + # create a copy of the pruned network dataset, which will be updated with merged data + data_merged = data.copy() + + # initialize list to store merged segment IDs + merged_segments = [] + + # organize network into reaches + conn = nhd_network.extract_connections(data, network_data["downstream_col"]) + rconn = nhd_network.reverse_network(conn) + subnets = nhd_network.reachable_network(rconn) + subreachable = nhd_network.reachable(rconn) + + subreaches = {} + for tw, net in subnets.items(): + path_func = partial(nhd_network.split_at_junction, net) + subreaches[tw] = nhd_network.dfs_decomposition(net, path_func) + + # loop through each reach in the network + for twi, (tw, rchs) in enumerate(subreaches.items(), 1): + + for rch in rchs: + + # calculate reach length + rch_len = data.loc[rch].Length.sum() + + ################################################## + # orphaned short single segment reaches + ################################################## + # if reach length is shorter than threshold and composed of a single segment + if rch_len < thresh and len(data.loc[rch]) == 1: + continue # do nothing + + ################################################## + # multi segment reaches - combine into a single segment reach + ################################################## + # if reach length is shorter than threshold and composed more than one segment + if rch_len < thresh and len(data.loc[rch]) > 1: + + # merge ALL reach segments into one + chop = [] + reach_merged, chop = merge_all(rch, data, chop) + + # update network with merged reach data + data_merged = update_network_data( + data_merged, + rch, + reach_merged, + chop, + rconn + ) + + # update merged_segmetns list with merged-out segments + merged_segments.extend(chop) + + ################################################## + # multi segment reaches longer than threshold with some segments shorter than threshold + ################################################## + # if reach length is longer than threshold and smallest segment length is less than threshold + if rch_len > thresh and data.loc[rch].Length.min() < thresh: + + # initialize data_merged - this DataFrame will be subsequently revised + reach_merged = data.loc[rch] + + # initialize list of segments chopped from this reach + chop_reach = [] + + # so long as the shortest segment is shorter than the threshold... + while reach_merged.Length.min() < thresh: + + # if shortest segment is the last segment in the reach - conduct an upstream merge. + if ( + reach_merged.Length.idxmin() == reach_merged.tail(1).index.values[0] + and reach_merged.Length.min() < thresh + ): + + # upstream merge + chop = [] + reach_merged, chop = upstream_merge(reach_merged, chop) + + # update chop_reach list with merged-out segments + chop_reach.extend(chop) + + # if shortest segment is NOT the last segment in the reach - conduct a downstream merge + if ( + reach_merged.Length.idxmin() != reach_merged.tail(1).index.values[0] + and reach_merged.Length.min() < thresh + ): + + # downstream merge + chop = [] + reach_merged, chop = downstream_merge(reach_merged, chop, thresh) + + # update chop_reach list with merged-out segments + chop_reach.extend(chop) + + # correct segment connections within reach + reach_merged = correct_reach_connections(reach_merged) + + # update the greater network data set + data_merged = update_network_data( + data_merged, + rch, + reach_merged, + chop_reach, + rconn + ) + + # update merged_segmetns list with merged-out segments + merged_segments.extend(chop_reach) + + # create a qlateral destinations dictionary + qlat_destinations = qlat_destination_compute(data_native, + data_merged, + merged_segments, + pruned_segments, + network_data) + + return data_merged, qlat_destinations \ No newline at end of file From 13ef1917d0301fdc0bcba7b69db5444dc8171ed8 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 4 Dec 2020 15:47:17 -0500 Subject: [PATCH 05/54] utilities and main function to prune, snap and merge network. modified RouteLink and crosswalk files are produced --- .../troute/nhd_network_augment.py | 575 +++++++++++++----- 1 file changed, 431 insertions(+), 144 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_augment.py b/src/python_framework_v02/troute/nhd_network_augment.py index 812088b02..989a58bc9 100644 --- a/src/python_framework_v02/troute/nhd_network_augment.py +++ b/src/python_framework_v02/troute/nhd_network_augment.py @@ -1,6 +1,115 @@ -import nhd_network import numpy as np +import pandas as pd +import xarray as xr from functools import partial +from itertools import chain +import os +import sys +import pathlib +import argparse +import json + +root = pathlib.Path("../../../").resolve() +sys.path.append(os.path.join(root, "src", "python_framework_v01")) + +import nhd_network_utilities_v02 as nnu +import nhd_network +import nhd_io +import network_dl + +def _handle_args(): + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument( + "-t", + "--threshold_length", + help = "threshold segment length (meters)", + dest = "threshold", + default = 500, + type = int, + ) + parser.add_argument( + "--network", + help="Choose from among the pre-programmed supernetworks (Pocono_TEST1, Pocono_TEST2, LowerColorado_Conchos_FULL_RES, Brazos_LowerColorado_ge5, Brazos_LowerColorado_FULL_RES, Brazos_LowerColorado_Named_Streams, CONUS_ge5, Mainstems_CONUS, CONUS_Named_Streams, CONUS_FULL_RES_v20, CapeFear_FULL_RES)", + dest="supernetwork", + choices=[ + "Pocono_TEST1", + "Pocono_TEST2", + "LowerColorado_Conchos_FULL_RES", + "Brazos_LowerColorado_ge5", + "Brazos_LowerColorado_FULL_RES", + "Brazos_LowerColorado_Named_Streams", + "CONUS_ge5", + "Mainstems_CONUS", + "CONUS_Named_Streams", + "CONUS_FULL_RES_v20", + "CapeFear_FULL_RES" + ], + default="CapeFear_FULL_RES", + ) + parser.add_argument( + "-p", + "--prune", + help = "prune short headwater reaches, 1 if yes, 0 if no", + dest = "prune", + action="store_true", + ) + parser.add_argument( + "-s", + "--snap", + help = "snap junctions adjacent to short reaches, 1 if yes, 0 if no", + dest = "snap", + action = "store_true" + ) + + return parser.parse_args() + +def get_network_data(network_name): + + # Create directory path variable for test/input/geo, where NHD data and masks are stored + test_folder = os.path.join(root, r"test") + geo_input_folder = os.path.join(test_folder, r"input", r"geo") + + # Load network meta data for the Cape Fear Basin + supernetwork = network_name + network_data = nnu.set_supernetwork_data( + supernetwork=supernetwork, geo_input_folder=geo_input_folder + ) + + # if the NHDPlus RouteLink file does not exist, download it. + if not os.path.exists(network_data["geo_file_path"]): + filename = os.path.basename(network_data["geo_file_path"]) + network_dl.download(network_data["geo_file_path"], network_data["data_link"]) + + # read-in NHD data, retain copies for viz- and full network analysis purposes + data = nhd_io.read(network_data["geo_file_path"]) + RouteLink = data.copy() # copy of full resolution CONUS network + + # select only the necessary columns of geospatial data, set the DataFrame index + cols = [v for c, v in network_data["columns"].items()] + # GET THE STRAHLER ORDER DATA TOO! + cols.append('order') + + data = nhd_io.read(network_data["geo_file_path"]) + data = data[cols] + data = data.set_index(network_data["columns"]["key"]) + + # mask NHDNetwork to isolate test network - full resolution Cape Fear basin, NC + if "mask_file_path" in network_data: + data_mask = nhd_io.read_mask( + network_data["mask_file_path"], layer_string=network_data["mask_layer_string"], + ) + data = data.filter(data_mask.iloc[:, network_data["mask_key"]], axis=0) + + # sort index + data = data.sort_index() + + # replace downstreams + data = nhd_io.replace_downstreams(data, network_data["columns"]["downstream"], 0) + + return data, RouteLink, network_data def network_connections(data, network_data): @@ -16,7 +125,7 @@ def network_connections(data, network_data): """ # extract downstream connections - conn = nhd_network.extract_connections(data, network_data["downstream_col"]) + conn = nhd_network.extract_connections(data, network_data["columns"]["downstream"]) # extract upstream connections rconn = nhd_network.reverse_network(conn) @@ -39,131 +148,200 @@ def build_reaches(rconn): return subreachable, subreaches, subnets -def headwater_connections(data, network_data): - - """ - Determine which segments are and are not headwaters. - Headwaters are defined as segments with no upstream connection, only downstream connections. - Non-headwaters are defined as segments with both upstream and downstream connections. - Args: - data (DataFrame): Network parameter dataset, prepared - Returns: - hw_conn (dict): downstream connections, headwaters only - non_hw_conn (dict): downstream connections, non headwaters only - """ - - # extract network connections - conn, rconn = network_connections(data, network_data) +def prune_headwaters(data, threshold, network_data): - hw = [] # store headwater segments - non_hw = [] # store non-headwater segments - - for seg in rconn.keys(): - # if there is no upstream connection anda downstream connection, it is a headwater - if bool(rconn[seg]) == False and bool(conn[seg]) == True: - hw.append(seg) - - # if there is an upstream connection and a downstream connection, it is a non-headwater (midwater?) - elif bool(rconn[seg]) == True and bool(conn[seg]) == True: - non_hw.append(seg) - - # get segment key-value pairs from the connections dictionary - hw_conn = {key: conn[key] for key in hw} - non_hw_conn = {key: conn[key] for key in non_hw} - - return hw_conn, non_hw_conn - -def prune_headwaters(hw_connections, non_hw_connections, data, thresh): - - # TO DO: Bug fix - why does the function return non-unique headwater IDs in the chop list? - # some headwaters are being identified for chopping more than once? + # initialize list to store pruned reaches + hw_prune_list = [] + + iter_count = 1 + while 1 == 1: + + # STEP 1: Find headwater reaches: + # --------------------------------------# + + # build connections and reverse connections + connections, rconn = network_connections(data.drop(index = chain.from_iterable(hw_prune_list)), network_data) + + # identify headwater segments + hws = connections.keys() - chain.from_iterable(connections.values()) + + # build reaches + subreachable, subreaches, subnets = build_reaches(rconn) + + # find headwater reaches + hw_reaches = [] + for sublists in list(subreaches.values()): + for rch in sublists: + for val in rch: + if val in hws: + hw_reaches.append(rch) + pass + + # STEP 2: identify short headwater reaches + # --------------------------------------# + # find headwater reaches shorter than threshold + short_hw_reaches = [] + for rch in hw_reaches: + if data.loc[rch,"Length"].sum() < threshold: + short_hw_reaches.append(rch) + + # CHECK: Are there any short headwter reaches? + # --------------------------------------# + if len(short_hw_reaches) == 0: + print("no more short headwaters to prune") + + # if no more reaches, exit while loop + break + + # STEP 3: trim short headwater reaches + # --------------------------------------# + hw_junctions = {} + for rch in short_hw_reaches: + hw_junctions[rch[-1]] = connections[rch[-1]] + + touched = set() + for i, (tw, jun) in enumerate(hw_junctions.items()): + touched.add(i) + + if list(hw_junctions.values()).count(jun) > 1: + # two short headwaters draining to the same junction + + # record reach1 as list of segments + reach1 = short_hw_reaches[i] + + for y, (tw_y, jun_y) in enumerate(hw_junctions.items()): + if jun_y == jun and tw_y != tw and y not in touched: + # the correlary headwater reach draining to the same junction has been found + reach2 = short_hw_reaches[y] + + # trim the shorter of two reaches + if data.loc[reach1,"Length"].sum() <= data.loc[reach2,"Length"].sum(): + # trim reach1 + hw_prune_list.append(reach1) + else: + # trim reach2 + hw_prune_list.append(reach2) + + if list(hw_junctions.values()).count(jun) == 1: + hw_prune_list.append(short_hw_reaches[i]) + + print("completed", iter_count, "iterations of headwater pruning") + iter_count += 1 + + data_pruned = data.drop(index = chain.from_iterable(hw_prune_list)) - """ - Prune headwaters from the network - Headwaters are pruned if they are shorter than the threshold length - OR if they merge with a midwater that is less than the threshold length - Args: - hw_connections (dict): downstream connections, headwaters only - non_hw_connections (dict): downstream connections, midwaters only - data (DataFrame): Network to be pruned - thresh (int): length threshold, segments below this length are slated for pruning - Returns: - chop (list): list of pruned headwater indices, updated - data_pruned (DataFrame): pruned network - """ + return data_pruned - # initialize a list of pruned headwater segments - chop = [] +def snap_junctions(data, threshold, network_data): - # loop through keys and values in headwater connections dictionary - for hw_k, srch_v in hw_connections.items(): - - """ - Trim headwater-headwater junctions - - - Headwater-headwater junctions are junctions where two (or more) headwaters merge. - - Once headwater-headwater junctions are identified, the shortest headwater below the - threshold length is pruned. - """ - - # create a list of headwaters draining to the specified downstream connections, srch_v - G = [k for k, v in hw_connections.items() if v == srch_v] - - # if more than one headwater drains into the segment, it is a headwater-headwater junction - if len(G) > 1: - - # get segment lengths from the test dataset - hw_len = data.loc[G].Length - - # find the shortest segment - hw_min_len = hw_len[hw_len.idxmin()] - - # check to see if headwater shorter than threshold length - if hw_min_len < thresh: + ''' + This function snaps junctions on oposite ends of a short reach + by forcing the lowest order upstream tributary to drain to the reach tail. + Short reaches are defined by a user-appointed threshold length. + + For example, consider a short reach (*) stranded between two junctions: + + \ / + \/ | 4th + 2nd \ | + \| / + *|/ + | + | + + The algoritm would select the lowest order tributary segment to the reach head, + which in this case is a second-order drainage, and change the downstream connection + to the reach tail. This produces the following "snapped" network config: + + \ / + \/ | + \ | / + \|/ + | + | + + Inputs + -------------- + - data (DataFrame): NHD RouteLink data, must contain Strahler order attribute + - this input can be either the native NHD data OR the data with pruned headwaters + + Outputs + -------------- + - snapped_data (DataFrame): NHD RouteLink data containing no short reaches + + ''' + # create a copy of the native data for snapping + data_snapped = data.copy() + + # snap junctions + iter_num = 1 + while 1 == 1: + + # evaluate connections + connections, rconn = network_connections(data_snapped, network_data) + + # build reaches + subreachable, subreaches, subnets = build_reaches(rconn) + + # build list of headwater segments + hws = connections.keys() - chain.from_iterable(connections.values()) + + # create a list of short reaches stranded between junctions + short_reaches = [] + for sublists in list(subreaches.values()): + for rch in sublists: + + # if reach is not a headwater + if len(np.setdiff1d(rch,list(hws))) == len(rch): + + # and if reach is shorter than threshold length + if data.loc[rch,"Length"].sum() < threshold: + + # consider it for junction snapping + short_reaches.append(rch) + + if iter_num > 1: + print("After iteration", iter_num - 1, ",", len(short_reaches), "short reaches remain") + else: + print("Prior to itteration", iter_num, ",", len(short_reaches), "short reaches exist in the network") + + # check that short reaches exist, if none - terminate process + if len(short_reaches) == 0: + break + + # for each short reach, snap lower order upstream trib to downstream drainage destination + for i, rch in enumerate(short_reaches): + + # identify reach tail (downstream-most) and head (upstream-most) segments + tail = rch[-1] + head = rch[0] + + # identify segments that drain to reach head + us_conn = rconn[head] + + # select the upstream segment to snap + o = data_snapped.loc[us_conn,'order'].tolist() # Strahler order + if min(o) != max(o): + # select the segment with lowest Strahler order + rch_to_move = data_snapped.loc[us_conn,'order'].idxmin() + else: + # if all segments are same Strahler order, select the shortest + rch_to_move = data_snapped.loc[us_conn,'Length'].idxmin() + + # snap destination is the segment that the reach tail drains to + snap_destination = connections[tail] + + # if reach tail doesn't have a downstrem connection (it is a network tailwater) + # then the snap desination is the ocean + if len(snap_destination) == 0: + snap_destination = data_snapped.loc[tail,'to'] + + # update RouteLink data with new tributary destination info + data_snapped.loc[rch_to_move,'to'] = snap_destination - # update chopping block - chop.append(hw_len.idxmin()) - - ############################################################ - # Trim headwater-midwater junctions - ############################################################ - - """ - Trim headwater-midwater junctions + iter_num += 1 - - Headwater-midwater junctions are junctions a headwater merges with a non-headwater (i.e. midwater). - - If the headwater is shorter than the threshold, it is pruned. - - If the headwater is longer than the threshold, but joins with or connects to a midwater that is - shorter than the threshold, the headwater is pruned. - - This is done to minimize the number of small midwater segments stranded between headwaters. - Else, these short, stranded segments cannot be merged upstream or downstream. - """ - - # midwaters draining into a junction that a headwater does too - H = [k for k, v in non_hw_connections.items() if v == srch_v] - - # if there is a corresponding midwater, then the headwater is a candidate for trimming - if bool(H) == True: - - # get segment lengths from the dataset - hw_len = data.loc[hw_k].Length - - # get midwater (in) length from the test dataset - mw_in_len = data.loc[H[0]].Length - - # get midwater (out) length from the test dataset - mw_out_len = data.loc[srch_v[0]].Length - - # check to see if headwater or midwaters (in/out) are shorter than the threshold - if hw_len < thresh or mw_in_len < thresh or mw_out_len < thresh: - - # update chopping block - chop.append(hw_k) - - # chop segments from the network - data_pruned = data.drop(np.unique(chop)) - - return np.unique(chop), data_pruned + return data_snapped def len_weighted_av(df, var, weight): @@ -345,15 +523,11 @@ def update_network_data(data, rch, data_merged, chop, rconn): data.loc[data_merged.index] = data_merged # update out of reach connections - these will change in the first segment was merged out - upstreams = rconn[ - data_old.head(1).index.values[0] - ] # upstream connection of the OLD reach head + upstreams = rconn[data_old.head(1).index.values[0]] # upstream connection of the OLD reach head if bool(upstreams): - data.loc[upstreams, "to"] = data_merged.head(1).index.values[ - 0 - ] # index of NEW reach head + data.loc[upstreams, "to"] = data_merged.head(1).index.values[0] # index of NEW reach head return data @@ -368,10 +542,11 @@ def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_s segments = merged_segments # compute connections - conn = nhd_network.extract_connections(data_native, network_data["downstream_col"]) + conn = nhd_network.extract_connections(data_native, network_data["columns"]["downstream"]) # initialize a library to store qlat desinations for pruned/merged segments - qlat_destinations = {x: {} for x in segments} +# qlat_destinations = {x: {} for x in str(segments)} + qlat_destinations = {} for idx in segments: @@ -383,7 +558,7 @@ def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_s ds_idx = conn[ds_idx[0]] # update the qlat destination dict - qlat_destinations[idx] = ds_idx + qlat_destinations[str(idx)] = str(ds_idx[0]) return qlat_destinations @@ -395,16 +570,11 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # initialize list to store merged segment IDs merged_segments = [] - # organize network into reaches - conn = nhd_network.extract_connections(data, network_data["downstream_col"]) - rconn = nhd_network.reverse_network(conn) - subnets = nhd_network.reachable_network(rconn) - subreachable = nhd_network.reachable(rconn) + # build connections and reverse connections + conn, rconn = network_connections(data, network_data) - subreaches = {} - for tw, net in subnets.items(): - path_func = partial(nhd_network.split_at_junction, net) - subreaches[tw] = nhd_network.dfs_decomposition(net, path_func) + # organize network into reaches + subreachable, subreaches, subnets = build_reaches(rconn) # loop through each reach in the network for twi, (tw, rchs) in enumerate(subreaches.items(), 1): @@ -426,7 +596,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): ################################################## # if reach length is shorter than threshold and composed more than one segment if rch_len < thresh and len(data.loc[rch]) > 1: - + # merge ALL reach segments into one chop = [] reach_merged, chop = merge_all(rch, data, chop) @@ -440,7 +610,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): rconn ) - # update merged_segmetns list with merged-out segments + # update merged_segments list with merged-out segments merged_segments.extend(chop) ################################################## @@ -483,7 +653,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # update chop_reach list with merged-out segments chop_reach.extend(chop) - + # correct segment connections within reach reach_merged = correct_reach_connections(reach_merged) @@ -496,7 +666,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): rconn ) - # update merged_segmetns list with merged-out segments + # update merged_segments list with merged-out segments merged_segments.extend(chop_reach) # create a qlateral destinations dictionary @@ -506,4 +676,121 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): pruned_segments, network_data) - return data_merged, qlat_destinations \ No newline at end of file + return data_merged, qlat_destinations + +def main(): + + args = _handle_args() + + supernetwork = args.supernetwork + threshold = args.threshold + prune = args.prune + snap = args.snap + + # get network data + print("Extracting and organizing supernetwork data") + data, RouteLink, network_data = get_network_data(supernetwork) + RouteLink = RouteLink.set_index(network_data["columns"]["key"]) + + pruned_segs = [] + + # prune headwaters + if prune and snap: + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.nc" + + print("Prune, snap, then merge:") + print("pruning headwaters...") + data_pruned = prune_headwaters(data, threshold, network_data) + + # identify pruned segments + pruned_segs = list(np.setdiff1d(data.index,data_pruned.index)) + + print("snapping junctions...") + data_snapped = snap_junctions(data_pruned, threshold, network_data) + + print("merging segments...") + data_merged, qlat_destinations = segment_merge( + data, + data_snapped, + network_data, + threshold, + pruned_segs + ) + + if snap and not prune: + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.nc" + + print("Snap and merge:") + print("snapping junctions...") + data_snapped = snap_junctions(data, threshold, network_data) + + print("merging segments...") + data_merged, qlat_destinations = segment_merge( + data, + data_snapped, + network_data, + threshold, + pruned_segs + ) + + if not snap and prune: + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.nc" + + print("Prune and merge:") + print("snapping junctions...") + data_snapped = snap_junctions(data, threshold, network_data) + + print("merging segments...") + data_merged, qlat_destinations = segment_merge( + data, + data_snapped, + network_data, + threshold, + pruned_segs + ) + + if not snap and not prune: + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.nc" + print("Just merge:") + print("merging segments...") + data_merged, qlat_destinations = segment_merge( + data, + data, + network_data, + threshold, + pruned_segs + ) + + # update RouteLink data + RouteLink_edit = RouteLink.loc[data_merged.index.values] + + print(RouteLink_edit.head()) + + for (columnName, columnData) in data_merged.iteritems(): + print(columnName) + RouteLink_edit.loc[:,columnName] = columnData + + for idx in RouteLink_edit.index: + if RouteLink_edit.loc[idx,'to'] < 0: + RouteLink_edit.loc[idx,'to'] = 0 + + # export as NetCDF + print("exporting RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) + DS = xr.Dataset.from_dataframe(RouteLink_edit) + DS.to_netcdf(os.path.join(root, "test", "input", "geo", "Channels", filename)) + + print(qlat_destinations) + + # save cross walk as json + print("exporting CrossWalk file:", filename_cw, "to", os.path.join(root, "test", "input", "geo", "Channels")) + with open(os.path.join(root, "test", "input", "geo", "Channels", filename_cw), "w") as outfile: + json.dump(qlat_destinations, outfile) + + +if __name__ == "__main__": + main() + From 15dcf56cd4253053c6704744474e8f88f21990e3 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 4 Dec 2020 16:15:49 -0500 Subject: [PATCH 06/54] add CapeFear test basin to supernetwork options --- .../troute/nhd_network_utilities_v02.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 715e940d2..677321886 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -23,6 +23,7 @@ def set_supernetwork_data( "Mainstems_CONUS", "CONUS_Named_Streams", "CONUS_FULL_RES_v20", + "CapeFear_FULL_RES", "custom", } @@ -286,6 +287,27 @@ def set_supernetwork_data( ) return rv + elif supernetwork == "CapeFear_FULL_RES": + rv = set_supernetwork_data( + supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder + ) + rv.update( + { + "title_string": "Cape Fear River Basin, NC", # overwrites other title... + "mask_file_path": os.path.join( + geo_input_folder, + "Channels", + "masks", + "CapeFear_FULL_RES.txt", + ), + "mask_driver_string": "csv", + "mask_layer_string": "", + "mask_key": 0, + "mask_name": 1, # TODO: Not used yet. + } + ) + return rv + elif supernetwork == "CONUS_FULL_RES_v20": ROUTELINK = "RouteLink_NHDPLUS" From 35ed6ee3a044aae77ceca3215a84b456a1fb4f03 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 4 Dec 2020 16:17:05 -0500 Subject: [PATCH 07/54] new mask for Cape Fear river basin, full resolution --- .../geo/Channels/masks/CapeFear_FULL_RES.txt | 15211 ++++++++++++++++ 1 file changed, 15211 insertions(+) create mode 100644 test/input/geo/Channels/masks/CapeFear_FULL_RES.txt diff --git a/test/input/geo/Channels/masks/CapeFear_FULL_RES.txt b/test/input/geo/Channels/masks/CapeFear_FULL_RES.txt new file mode 100644 index 000000000..1ca4e6e81 --- /dev/null +++ b/test/input/geo/Channels/masks/CapeFear_FULL_RES.txt @@ -0,0 +1,15211 @@ +8834820 +8832574 +8833996 +8834000 +8832730 +8832674 +8832666 +8832664 +8832654 +8832656 +8832652 +8838478 +8832642 +8832614 +8832620 +8834990 +8832446 +8832660 +8834102 +8832562 +8834104 +8834780 +8834764 +8834766 +8834112 +8834124 +8834142 +8834156 +8834172 +8833628 +8834992 +8834524 +8832386 +8833788 +8832608 +8833790 +8833800 +8833804 +8832542 +8832710 +8832550 +8832746 +8832742 +8835706 +8832458 +8834304 +8834290 +8834282 +8834278 +8834270 +8832454 +8834832 +8834212 +8834840 +8834858 +8834868 +8834864 +8834848 +8832570 +8834762 +8834162 +8834150 +8834164 +8834154 +8834144 +8834114 +8834784 +8832700 +8834070 +8833930 +8833884 +8834648 +8834676 +8833938 +8832548 +8835074 +8834616 +8833840 +8833666 +8835324 +8835322 +8834298 +8834302 +8834280 +8832724 +8832576 +8835320 +8835364 +8835388 +8835318 +8835360 +8832682 +8832578 +8832452 +8832676 +8832678 +8832706 +8834228 +8834836 +8834844 +8832704 +8834258 +8834260 +8834262 +8834264 +8832572 +8832568 +8832668 +8832670 +8832448 +8832672 +8834122 +8834158 +8834160 +8832564 +8834072 +8832422 +8832552 +8832634 +8834658 +8832396 +8834662 +8835122 +8832394 +8833890 +8834646 +8834664 +8833904 +8833900 +8833916 +8832616 +8834580 +8834600 +8833822 +8833838 +8832546 +8838458 +8833680 +8832602 +8832392 +8832560 +8832556 +8832558 +8832554 +8832444 +8833964 +8833986 +8834690 +8834024 +8834696 +8832432 +8832428 +8834208 +8835390 +8834604 +8832390 +8832622 +8832626 +8832426 +8832650 +8832648 +8832424 +8832420 +8832566 +8832658 +8832662 +8832440 +8832436 +8833982 +8833984 +8833994 +8834006 +8834004 +8833998 +8832708 +8832594 +8832720 +8832722 +8832718 +8832632 +8832644 +8832418 +8832636 +8832638 +8832414 +8832410 +8832406 +8832402 +8832734 +8832738 +8835326 +8835374 +8835370 +8835400 +8835444 +8835368 +8835372 +8835446 +8835380 +8835404 +8835376 +8835384 +8838470 +8835378 +8835382 +8834998 +8832646 +8833972 +8838388 +8834994 +8832596 +8832740 +8832612 +8834210 +8838440 +8832450 +8832680 +8835402 +8835350 +8835396 +8835718 +8834204 +8832732 +8833970 +8832456 +8832580 +8832702 +26327800 +10520342 +8832716 +8832640 +8832630 +8832606 +8832598 +8834996 +8832600 +8835724 +8835366 +8838468 +8835398 +8832462 +8835394 +8835348 +8832588 +8834232 +8833810 +8832736 +8838480 +8832398 +8832628 +8838474 +8832604 +8832434 +8832714 +8832712 +8832698 +8835358 +8835362 +26327822 +8832460 +8838460 +8833988 +8832586 +8832388 +8832744 +8832610 +8832618 +8838476 +8832624 +8832748 +8832684 +8832686 +8832688 +8832690 +8832692 +8832694 +8832696 +8832726 +8832728 +8835028 +8835030 +8835052 +8835054 +8835056 +8835058 +8835068 +10526727 +10529081 +10529087 +10529093 +10529091 +10529073 +10529063 +10529069 +10529055 +10529061 +10529031 +10529027 +10529023 +10526677 +10526647 +10527367 +10526673 +10526679 +10526687 +10526699 +10529517 +10529663 +10529487 +10529483 +10529457 +10528383 +10528055 +10528375 +10526713 +10526685 +10528053 +10526711 +10526705 +10526703 +10526667 +10526643 +10527377 +10526683 +10527373 +10526671 +10526651 +10526649 +10527349 +10527483 +10528843 +10528863 +10528957 +10528861 +10528871 +10528829 +10526669 +10528857 +10529653 +10529541 +10529485 +10529455 +10529377 +10528059 +10526709 +10526717 +10526733 +10526723 +10526719 +10526721 +10526665 +10526693 +10528377 +10528805 +10528827 +10528815 +10528807 +10529383 +10529409 +10529651 +10529117 +10528969 +10529113 +10529115 +10528845 +10528831 +10527389 +10527387 +10526737 +10526747 +10527395 +10527381 +10527383 +10527369 +10527403 +10529143 +10532863 +10529017 +10528973 +10529039 +10529037 +10528851 +10532847 +10529025 +10529059 +10528849 +10528979 +10528981 +10528995 +10529149 +10529107 +10532855 +10529147 +10529095 +10529097 +10529103 +10529105 +10528993 +10528991 +10528841 +10532849 +10529045 +10529053 +10528835 +10528965 +10529459 +10529463 +10529473 +10529471 +10529475 +10529493 +10528859 +10529077 +10529075 +10528853 +10529071 +10529065 +10528971 +10529679 +10528381 +10528057 +10528419 +10526701 +10526663 +10528051 +10529683 +10529519 +10529511 +10529507 +10529495 +10529489 +10528825 +10529467 +10528823 +10528819 +10528817 +10528813 +10528809 +10528811 +10528821 +10529469 +10529411 +10529497 +10529509 +10529673 +10528839 +10529145 +10529049 +10528967 +10528833 +10529033 +10528837 +10529041 +10529029 +10527447 +10527443 +10527399 +10527391 +10527397 +10527371 +10527363 +10526715 +10526655 +10526635 +10527471 +10526661 +10526697 +10526681 +10526689 +10527365 +10527375 +10526695 +10526731 +10527385 +10528847 +10528959 +10528963 +10529015 +10528855 +10528975 +10528977 +10528983 +10528865 +10528985 +10528867 +10528989 +10529003 +10529007 +10528875 +10528869 +10529001 +10528877 +10527393 +10529141 +10526745 +10529337 +10527417 +10526675 +10526637 +10526657 +10526639 +10526633 +10527353 +10527401 +10526735 +10529119 +10528961 +10529079 +10528987 +10529085 +10529083 +10529089 +10528999 +10528997 +10529005 +10528873 +10529101 +10529099 +10529009 +10532857 +10529109 +10526707 +10529125 +10527451 +10526741 +10529111 +10529129 +10529137 +10532861 +10529127 +10532859 +10529139 +10526653 +10526743 +10529477 +10529461 +10529501 +10529491 +10529505 +10529499 +10529057 +10532851 +10529047 +10529151 +10526749 +10528801 +10532765 +10526729 +10527441 +10526691 +10527445 +10527449 +10527485 +10527489 +10529051 +10529677 +10529675 +10529681 +10529479 +10529339 +10526659 +10527379 +10529481 +10529503 +10526739 +10529043 +10529133 +10529135 +10529131 +10532767 +10528803 +10529121 +10529123 +10529067 +10529035 +10532853 +10529153 +8834614 +8833606 +8834120 +8834584 +8833734 +8834818 +8834880 +8835102 +8834066 +8834046 +8834040 +8834052 +8834748 +8834082 +8834084 +8834074 +8834034 +8834032 +8834026 +8834008 +8833992 +8834692 +8833976 +8833962 +8833954 +8833948 +8833936 +8833934 +8833398 +8834958 +8833400 +8833432 +8834494 +8833476 +8833506 +8834530 +8833656 +8833654 +8833670 +8833730 +8833768 +8835094 +8835000 +8834582 +8833806 +8833814 +8833592 +8833572 +8833602 +8834022 +8834036 +8834732 +8834080 +8834106 +8834598 +8833830 +8834602 +8834292 +8835696 +8834294 +8834878 +8834882 +8834310 +8834318 +8834860 +8834876 +8834874 +8834872 +8834866 +8834862 +8835076 +8834268 +8833968 +8834126 +8834742 +8834756 +8834776 +8835108 +8834812 +8834790 +8834794 +8835114 +8834096 +8834094 +8834774 +8834810 +8834254 +8834206 +8834166 +8834826 +8834194 +8834200 +8834216 +8834226 +8834854 +8834856 +8834250 +8834248 +8834852 +8834838 +8834256 +8834842 +8834846 +8834824 +8834202 +8835116 +8834816 +8834798 +8834180 +8834186 +8834174 +8834792 +8834118 +8834108 +8833990 +8835096 +8835098 +8834700 +8834638 +8834670 +8834668 +8834656 +8834632 +8833896 +8833910 +8833958 +8833950 +8833942 +8833940 +8833952 +8833928 +8834654 +8834650 +8834666 +8833932 +8833966 +8834010 +8834712 +8834012 +8834092 +8834016 +8834736 +8834770 +8834090 +8834778 +8834076 +8834078 +8834726 +8834728 +8834746 +8834644 +8833944 +8834640 +8833908 +8833914 +8834672 +8834682 +8834758 +8834170 +8834140 +8834184 +8833898 +8834634 +8833920 +8834674 +8833698 +8833676 +8833696 +8835072 +8834558 +8833834 +8833842 +8833482 +8833558 +8833554 +8833608 +8834534 +8833450 +8833466 +8834490 +8833454 +8833456 +8833468 +933030016 +8833428 +8833498 +8833762 +8834592 +8834622 +8834610 +8833694 +8833646 +8833494 +8833394 +8833552 +8833686 +8833620 +8833662 +8833704 +8834556 +8833816 +8833828 +8835092 +8834596 +8833732 +8833624 +8834480 +8834306 +8834314 +8835694 +8834300 +8834296 +8835692 +8834870 +8834284 +8834286 +8835060 +8835062 +8834288 +8835124 +8834276 +8834274 +8834272 +8834266 +8834702 +8834064 +8834088 +8834116 +8834130 +8834132 +8834134 +8834788 +8835104 +8835018 +8834110 +8834138 +8834804 +8834128 +8834786 +8835020 +8835110 +8834100 +8834814 +8834146 +8834168 +8834178 +8834830 +8834198 +8834214 +8834234 +8834236 +8834238 +8834244 +8834246 +8834802 +8834188 +8834822 +8834190 +8834806 +8834242 +8834252 +8834240 +8834222 +8834220 +8834224 +8834850 +8834230 +8834218 +8834828 +8834796 +8834148 +8834176 +8834182 +8835112 +8835016 +8835106 +8834782 +8834738 +8834720 +8835012 +8834730 +8834688 +8833956 +8833922 +8833902 +8833894 +8833892 +8833924 +8833974 +8833960 +8833946 +8833926 +8834038 +8834028 +8834710 +8834030 +8834062 +8834740 +8834718 +8834018 +8834020 +8834086 +8834722 +8834772 +8834056 +8834068 +8834058 +8834048 +8834050 +8834060 +8834054 +8834042 +8834044 +8834002 +8833980 +8833978 +8834678 +8834684 +8834680 +8833912 +8833906 +8834694 +8835014 +8835100 +8834136 +8834152 +8833888 +8833918 +8833726 +8833720 +8834544 +8833702 +8833560 +8833556 +8833540 +8833430 +8834964 +8833460 +8833458 +8833544 +8833504 +8834516 +8833470 +8833452 +8833442 +8834512 +8834984 +8834496 +8834988 +8833446 +8833588 +8834532 +8833480 +8833496 +8834508 +8834520 +8833706 +8834564 +8834594 +8833766 +8833648 +8833716 +8833722 +8833678 +8833650 +8833360 +8834942 +8833380 +8833384 +8833390 +8834472 +8835088 +8833640 +8833760 +8834944 +8834950 +8834952 +8833362 +8834974 +8834976 +8834972 +8833382 +8834982 +8834966 +8834968 +26327857 +8835086 +8834476 +8835120 +8834946 +8834960 +8834948 +8835126 +8834978 +8834192 +8833542 +8833420 +933030017 +8834954 +8834980 +8834196 +8833410 +8834478 +8833402 +8835128 +8834970 +26327860 +8834986 +8834492 +8833434 +26819924 +8835002 +8835004 +8835006 +8835008 +8835010 +8835032 +8835040 +8835042 +8835044 +8835046 +8835048 +8835050 +8835064 +8835066 +8835070 +8835034 +8835036 +8835038 +8835022 +8835024 +8835026 +11562792 +8818403 +8818435 +8818437 +8818427 +8818419 +8818421 +8818431 +8818393 +8817859 +8818387 +8817799 +8817735 +8818321 +8817399 +8817353 +8817189 +8818287 +8817031 +8817091 +8816979 +8816971 +8816947 +8816905 +8818451 +8815551 +8818243 +8815547 +8815535 +8815529 +8818289 +8817149 +8817021 +8817135 +8818273 +8816997 +8816975 +8818263 +8818261 +8818255 +8815549 +8817539 +8817613 +8817621 +8818385 +8817519 +8817521 +8817537 +8817549 +8817551 +8817527 +8817529 +8817607 +8817577 +8817599 +8817601 +8817585 +8817587 +8817523 +8817461 +8817439 +8817411 +8817397 +8817385 +8817369 +8817339 +8817331 +8817333 +8817335 +8817343 +8817327 +8817319 +8818115 +8818113 +8818095 +8818067 +8818059 +8818069 +8818035 +8818029 +8818037 +8818019 +8817487 +8817499 +8817531 +8817535 +8817533 +8817543 +8817569 +8817581 +8818271 +8818073 +8818091 +8818117 +8818119 +8818003 +8817917 +8817201 +8816935 +8817073 +8817027 +8816883 +8816899 +8815541 +8815553 +8816943 +8816887 +8818043 +8818063 +8818071 +8818081 +8818093 +8818111 +8818349 +8818079 +8818041 +8818077 +8818087 +8818083 +8818061 +8818075 +8818097 +166755108 +166755107 +8818121 +8818007 +8818423 +8818405 +8818001 +8817995 +8818411 +8818413 +8818401 +8817317 +8817321 +8817315 +8817329 +8817347 +8817363 +8817359 +8817377 +8817379 +8817375 +8817357 +8817345 +8817349 +8817351 +8817373 +8817371 +8817401 +8817437 +8817441 +8817463 +8817603 +8817593 +8817609 +8817589 +8817541 +8817567 +8817557 +8817559 +8817553 +8817561 +8818363 +8818365 +8817133 +8817033 +8817029 +8817001 +8816951 +8816933 +8816953 +8817003 +8816919 +8815555 +8816917 +8815543 +8816311 +8816945 +8816925 +8816891 +8816893 +8816903 +8816901 +8816927 +8818291 +8818295 +8817413 +8817213 +8817053 +8818267 +8818247 +8815527 +8815501 +8815497 +8815537 +8818249 +8818259 +8818265 +8817191 +8817185 +8818323 +8817849 +8816967 +8817779 +8818099 +8818089 +8816969 +8816999 +8818293 +8818371 +8818367 +8817323 +8818379 +8818389 +8818319 +8818285 +8817067 +8816977 +8817051 +8817025 +8815499 +8817355 +8818325 +8818375 +8817897 +8817979 +8817815 +8818397 +8818395 +8818329 +8818425 +8818015 +8818417 +8818415 +8818433 +8818123 +8818429 +8818445 +8818443 +8818127 +8817811 +8818381 +8818383 +8817555 +8817511 +8817465 +8817479 +8817497 +8817571 +8817583 +8817517 +8817503 +8818407 +8818353 +8818453 +8818441 +8818447 +8818449 +8818409 +8818399 +8818391 +8818297 +8818377 +8818373 +8818361 +8818369 +8817525 +8817505 +8818439 +8818347 +8817489 +8817579 +166755109 +8834934 +8835118 +10529427 +10529423 +10529403 +10529395 +10529393 +10529391 +10529385 +10529381 +10529373 +10529371 +10529361 +10529365 +10528061 +10529515 +10529659 +10529379 +10528049 +10528387 +10529359 +10529335 +10529387 +10529407 +10529647 +10529413 +10529437 +10529435 +10529441 +10529443 +10529449 +10529451 +10529425 +10529415 +10529421 +10529419 +10529417 +10529389 +10528379 +10528063 +10529349 +10529375 +10529397 +10529399 +10529405 +10529401 +10529447 +10529649 +10529521 +10529433 +10529445 +10529431 +10529465 +10529453 +10529439 +10529667 +10529669 +10529429 +10528385 +10529671 +10529529 +10529525 +10529531 +10529533 +10529665 +10528065 +10529357 +8834808 +11562784 +11562696 +11562776 +8834686 +8831198 +8834932 +8834918 +8834912 +8834904 +8834892 +8834898 +8834908 +8831858 +8834894 +8834014 +8834724 +8834768 +8833386 +8834486 +8834506 +8833526 +8833486 +8834504 +8833518 +8833462 +8834502 +8834498 +8833578 +8833548 +8833536 +8834484 +8834500 +8833474 +8833576 +8833616 +8833664 +8833668 +8833712 +8834606 +8834608 +8834588 +8833844 +8833836 +8834660 +8833886 +8833866 +8834642 +8833742 +8833690 +8834546 +8833682 +8833672 +8833638 +8833612 +8833622 +8833484 +8833448 +8833438 +8833440 +8833444 +8833464 +8833488 +8833492 +8833502 +8834522 +8833570 +8833520 +8833478 +8833586 +8833610 +8833618 +8833644 +8833674 +8833684 +8833700 +8833710 +8833718 +8833728 +8834554 +8833770 +8833764 +8833636 +8833626 +8833614 +8833580 +8833574 +8833550 +8833538 +8833516 +8833508 +8833472 +8833634 +8833632 +8833660 +8834586 +8833824 +8833826 +8833832 +8834612 +8833864 +8834624 +8833868 +8833862 +8833708 +8834550 +8833688 +8833582 +8833596 +8833590 +8833376 +8834466 +8833316 +8833302 +8833300 +8833314 +8833324 +8834450 +8834488 +8833344 +8834468 +8833422 +8833396 +8834482 +8833296 +8834448 +8833288 +8835078 +8833272 +8833270 +8833282 +8834440 +8834434 +8833292 +8833256 +8834436 +8831634 +8831632 +8831874 +8831220 +8831876 +8831202 +8831232 +8833260 +8834438 +8833298 +8833794 +8833750 +8834940 +8834458 +8834936 +8833318 +8833312 +8833310 +8833320 +8833332 +8833356 +8833358 +8833342 +8833374 +8833364 +8833378 +8833414 +8833412 +8833290 +8834926 +8834920 +8834446 +8834442 +8834914 +8834910 +8834444 +8834922 +8833284 +8833280 +8833274 +8833276 +8833278 +8834916 +8833266 +8833286 +8835080 +8834900 +8834896 +8831636 +8831854 +8831214 +8831216 +8831850 +8831210 +8831630 +8831224 +8831856 +8831228 +8831230 +8831844 +8831842 +8833254 +8834890 +8834902 +8834906 +8833268 +8833306 +8834928 +8834452 +8834464 +8833774 +8831852 +8834924 +8834938 +8833330 +8834962 +8834930 +8831846 +8831848 +8833692 +8835082 +8835084 +8818257 +8818251 +8815539 +8816249 +8818327 +8817303 +8817295 +8817211 +8817199 +8817193 +8817187 +8817179 +8818299 +8817141 +8817057 +8815545 +8815559 +8815521 +8818009 +8818023 +8818027 +8818021 +8817981 +8817219 +8817229 +8817227 +8817235 +8817241 +8817239 +8817257 +8817247 +8817251 +8817267 +8817271 +8817269 +8817275 +8817281 +8817285 +8816907 +8818331 +8815525 +8815511 +8815515 +8815513 +8815523 +8815517 +8817143 +8817181 +8817197 +8817205 +8817223 +8817195 +8817203 +8818311 +8817309 +8815531 +8816309 +8815519 +8816251 +8816885 +8815507 +8816923 +8816909 +8816889 +8816897 +8816895 +8816911 +8816959 +8816921 +8817207 +8817217 +8817215 +8817225 +8817237 +8817209 +8817221 +8818305 +8818307 +8817279 +8817245 +8817277 +8817263 +8817265 +8817253 +8817261 +8817287 +8815557 +8816877 +8816879 +8816257 +8818309 +8817243 +8817259 +8817283 +8833294 +8831212 +8831222 +8831226 +8831218 +8831200 +8831208 +8831206 +8872838 +8871150 +8871040 +8871260 +8871644 +8875566 +8876120 +8871242 +8874680 +8871350 +8872110 +8871024 +8872104 +8871658 +8871610 +8871604 +8871596 +8871554 +8871502 +8871494 +8872122 +8874956 +8874928 +8874912 +8874908 +8874874 +8874862 +8874852 +8874810 +8874794 +8874760 +8874724 +8874696 +8874702 +8874676 +8876146 +8871896 +8874618 +8872154 +8871720 +8875186 +8875228 +8875224 +8875322 +8875304 +8875974 +8875356 +8875620 +8875574 +8875642 +8875414 +8875496 +8875406 +8875372 +8875500 +8875666 +8875690 +8875706 +8875738 +8871048 +8871102 +8871128 +8872114 +8871096 +8871222 +8871510 +8871420 +8871382 +8871366 +8875554 +8875630 +8875662 +8875686 +8875996 +8874634 +8875018 +8875136 +8874926 +8874832 +8874830 +8874850 +8874808 +8876040 +8874694 +8874698 +8872888 +8872886 +8875802 +8874612 +8875806 +8874730 +8874766 +8874888 +8875812 +8876048 +8874998 +8874966 +8874934 +8874954 +8875066 +8875124 +8875202 +8876050 +8875138 +8875106 +8875070 +8875072 +8876044 +8875088 +8875144 +8875178 +8875494 +8875182 +8875190 +8875196 +8875218 +8875266 +8875274 +8875286 +8875276 +8875316 +8875328 +8875388 +8875378 +8875588 +8875420 +8875872 +8875864 +8875612 +8875332 +8875336 +8875542 +8876110 +8876098 +8876096 +8876092 +8876114 +8876116 +8876108 +8876094 +8876088 +8875404 +8875312 +8875514 +8875394 +8876080 +8875568 +8875476 +8875556 +8875610 +8876100 +8876104 +8876102 +8874574 +8876032 +8874564 +8874750 +8871670 +8871702 +8871818 +8871894 +8874558 +8874854 +8874902 +8874896 +8874932 +8875820 +8874970 +8874988 +8875016 +8875048 +8875056 +8875076 +8875128 +8875130 +8875826 +8874886 +8874722 +8871780 +8871724 +8874640 +8874642 +8875064 +8875134 +8874994 +8875214 +8875192 +8875194 +8875216 +8875254 +8875264 +8875284 +8875280 +8875288 +8875302 +8875324 +8875858 +8875840 +8875976 +8875368 +8875364 +8875390 +8875434 +8875518 +8875462 +8875502 +8875558 +8875718 +8875712 +8876122 +8875262 +8875652 +8875660 +8875654 +8875658 +8875650 +8875692 +8876002 +8875698 +8875998 +8876000 +8875688 +8875578 +8875626 +8875994 +8875668 +8875638 +8875392 +8875416 +8875478 +8875432 +8875510 +8875528 +8875694 +8875700 +8875708 +8875704 +8874620 +8874576 +8874566 +8870964 +8870944 +8871036 +8871044 +8871042 +8872848 +8871192 +8871210 +8871230 +8871202 +8871304 +8871334 +8871504 +8871346 +8872874 +8871280 +8871236 +8871288 +8871534 +8871340 +8871552 +8872138 +8871598 +8872882 +8872880 +8871766 +8871786 +8871770 +8872878 +8871892 +8871832 +8874598 +8871838 +8871834 +8871008 +8871038 +8871142 +8870956 +8871092 +8871098 +8871076 +8871136 +8871660 +8871588 +8871460 +8871458 +8871418 +8871380 +8871348 +8871342 +8872126 +8871462 +8872128 +8871500 +8871612 +8871608 +8872144 +8872506 +8871624 +8871592 +8871634 +8871632 +8871674 +8871716 +8871652 +8871664 +8871820 +8871874 +8872516 +8876144 +8871752 +8871744 +8875720 +8872508 +8872510 +8872512 +8870982 +8875950 +8875958 +8875954 +8875928 +8875932 +8876136 +8875942 +8875944 +8875938 +8875940 +8872158 +8875924 +8876140 +8872156 +8871750 +8872150 +8871686 +8871668 +8871640 +8871496 +8871488 +8871398 +8871370 +8871344 +8872116 +8871154 +8871094 +8872112 +8871026 +8872098 +8871156 +8871078 +8871046 +8872108 +8872096 +8875456 +8875774 +8875314 +8876078 +8871070 +8871000 +8871050 +8872106 +8871746 +8871728 +8871698 +8871672 +8874740 +8875946 +8875948 +8874742 +8874836 +8874870 +8874946 +8874948 +8875006 +8875822 +8875052 +8875050 +8875026 +8874980 +8874996 +8874962 +8875098 +8875126 +8875142 +8875146 +8875150 +8875572 +8875594 +8875618 +8875352 +8875856 +8875382 +8875422 +8875444 +8875452 +8875454 +8875426 +8875436 +8875460 +8875866 +8875512 +8875520 +8875522 +8875538 +8875552 +8875614 +8875624 +8875640 +8875646 +8876148 +8875670 +8876150 +8875680 +8875682 +8875710 +8875722 +8875730 +8875742 +8875750 +8875576 +8875674 +8875714 +8875724 +8875732 +8875740 +8875744 +8875754 +8875772 +8875770 +8874752 +8875930 +8874878 +8874986 +8875038 +8875034 +8875972 +8875078 +8875118 +8875154 +8874550 +8876138 +8871882 +8874532 +8871762 +8871446 +8872858 +8875250 +8875428 +8875524 +8875506 +8875546 +8871486 +8871434 +8871412 +8871376 +8871310 +8871224 +8871188 +8871160 +8871162 +8871170 +8871116 +8874662 +8875926 +8874668 +8874628 +8874596 +8874624 +8874630 +8874638 +8874650 +8876070 +8875810 +8875816 +8875054 +8875020 +8875458 +8875084 +8875170 +8875480 +8875204 +8876046 +8875104 +8875102 +8874848 +8874882 +8874866 +8874942 +8875024 +8874978 +8874972 +8874952 +8875094 +8875148 +8875734 +8876086 +8875590 +8876074 +8876062 +8876060 +8876056 +8875308 +8875232 +8875270 +8875116 +8875040 +8874976 +8876026 +8874590 +8874610 +8874608 +8874636 +8874778 +8874820 +8876034 +8874816 +8874768 +8875562 +8875564 +8875982 +8875536 +8875540 +8875544 +8875986 +8875988 +8874790 +8874804 +8875960 +8874834 +8874968 +8875956 +8875952 +8874774 +8875082 +8875132 +8875320 +8875340 +8875152 +8874872 +8875656 +8875532 +8875470 +8875440 +8875354 +8875468 +8875442 +8875100 +8875160 +8875242 +8875112 +8875008 +8874876 +8874746 +8874578 +8874626 +8874654 +8874706 +8874674 +8874714 +8874780 +8871684 +8871722 +8872876 +8871468 +8872862 +8871318 +8871306 +8871246 +8872120 +8872824 +8871152 +8871158 +8871218 +8871248 +8871264 +8871212 +8870988 +8870984 +8870976 +8870948 +8871002 +8871666 +8871436 +8872502 +8872500 +8871404 +8871396 +8871308 +8871220 +8871134 +8871068 +8871414 +8871368 +8871124 +8871066 +8871014 +8871058 +8871022 +8871088 +8870934 +8871386 +8871432 +8871454 +8871574 +8872884 +8871822 +8871890 +8871808 +8871848 +8871760 +8871448 +8871700 +8874604 +8872152 +8872146 +8872142 +8871450 +8871356 +8871400 +8870972 +8871020 +8871080 +8871316 +8871228 +8871298 +8871498 +8871642 +8871636 +8871764 +8871814 +8872514 +8871804 +8871878 +8871888 +8876142 +8874666 +8875934 +8875936 +8874552 +8871886 +8875908 +8871456 +8871884 +8874534 +8874678 +8875922 +8874602 +8871880 +8871742 +8871706 +8871570 +8871526 +8872140 +8872134 +8872132 +8871748 +8871584 +8871464 +8871620 +8874710 +8871816 +8875832 +8875140 +8875222 +8875836 +8875846 +8875300 +8875278 +8875362 +8875384 +8876052 +8875446 +8875486 +8875862 +8875508 +8875526 +8875516 +8876076 +8875636 +8875726 +8876084 +8875648 +8875702 +8875606 +8875762 +8875786 +8875782 +8876118 +8875748 +8875736 +8875696 +8875676 +8875616 +8875632 +8876090 +8876106 +8876128 +8876126 +8876124 +8876112 +8875760 +8875768 +8875752 +8875728 +8875878 +8875534 +8875472 +8871514 +8871708 +8875852 +8875424 +8875570 +8875990 +8875672 +8875684 +8875992 +8875716 +8875678 +8875758 +8875766 +8875746 +8875756 +8875778 +8876010 +8875784 +8875776 +8875780 +8875764 +8875882 +8875586 +8875880 +8875602 +8875604 +8875664 +8875600 +8872102 +8870990 +8875978 +8875980 +8875984 +8876004 +8876006 +8876008 +8849567 +8850553 +8849627 +8849629 +8849573 +8849307 +8850497 +8849593 +8850545 +8849625 +8849729 +8849703 +8850563 +8851155 +8849309 +8849607 +8849759 +8851181 +8849431 +8849511 +8851127 +8849351 +8849345 +8849349 +8851221 +8849337 +8849479 +8851223 +8849347 +8849447 +8850765 +8850767 +8850769 +8870974 +8870958 +8870996 +8871072 +8871144 +8871140 +8871130 +8871056 +8871060 +8871052 +8870986 +8874692 +8874684 +8874732 +8874846 +8874806 +8874800 +8874788 +8874718 +8874584 +8874546 +8874586 +8874616 +8874614 +8875892 +8875906 +8875900 +8875910 +8876132 +8875916 +8875962 +8875964 +8871108 +8876064 +8874826 +8874892 +8874898 +8874904 +8874936 +8874982 +8875046 +8875090 +8875108 +8875168 +8875184 +8875238 +8875272 +8875330 +8875096 +8875200 +8875838 +8875844 +8875318 +8875854 +8875358 +8875366 +8875402 +8874688 +8874744 +8874796 +8874838 +8874984 +8875074 +8871358 +8871272 +8871190 +8871176 +8875398 +8875860 +8875482 +8875484 +8875504 +8875530 +8875560 +8875580 +8875584 +8875582 +8871360 +8871270 +8871112 +8871122 +8871184 +8871254 +8871330 +8871438 +8872850 +8871524 +8871390 +8871546 +8871622 +8871600 +8871696 +8871796 +8874540 +8874672 +8874734 +8874582 +8871828 +8871830 +8871758 +8874738 +8874782 +8874764 +8874822 +8874880 +8874958 +8875086 +8874560 +8871872 +8875896 +8875894 +8871870 +8871868 +8880516 +8871732 +8871680 +8871476 +8871406 +8871178 +8871146 +8871148 +8871226 +8871392 +8871326 +8871542 +8871522 +8871536 +8871538 +8871548 +8871532 +8871490 +8871516 +8871572 +8871714 +8871844 +8874544 +8871862 +8874588 +8871866 +8874660 +8875902 +8875904 +8875912 +8875914 +8875966 +8874860 +8875004 +8871354 +8874600 +8871694 +8871474 +8871372 +8871512 +8871544 +8871756 +8871688 +8874708 +8874704 +8874776 +8874884 +8874906 +8874916 +8874890 +8874842 +8876036 +8875000 +8874974 +8875092 +8875114 +8875164 +8875248 +8875842 +8875848 +8875622 +8876082 +8876072 +8875598 +8876066 +8875592 +8875868 +8875870 +8875628 +8875634 +8875644 +8876068 +8875596 +8876058 +8875490 +8875430 +8875418 +8875408 +8875386 +8875448 +8875412 +8875438 +8875492 +8875282 +8875246 +8875180 +8875012 +8874944 +8875344 +8875080 +8875208 +8875376 +8875474 +8875464 +8875466 +8875342 +8875226 +8875298 +8875022 +8874950 +8876030 +8875918 +8875920 +8875898 +8874542 +8876134 +8871876 +8874528 +8874526 +8871730 +8871408 +8871682 +8871594 +8871614 +8871864 +8871782 +8871784 +8874802 +8874938 +8874920 +8874914 +8874840 +8875044 +8875240 +8875210 +8875498 +8875874 +8875876 +8875608 +8875548 +8875550 +8875850 +8875346 +8875350 +8875348 +8875396 +8875450 +8875488 +8876054 +8875244 +8875028 +8875110 +8875236 +8875252 +8875370 +8875306 +8875410 +8875122 +8875166 +8875206 +8875156 +8874818 +8874824 +8874686 +8872802 +8870970 +8871032 +8871084 +8871214 +8872834 +8872836 +8871282 +8870940 +8870954 +8871062 +8871004 +8870968 +8870966 +8871016 +8871054 +8870978 +8871208 +8871206 +8871204 +8871252 +8871262 +8871286 +8850041 +8850057 +8850121 +8849407 +8849423 +8849413 +8849405 +8849375 +8849385 +8849451 +8850541 +8849527 +8849559 +8849583 +8849601 +8849597 +8849621 +8849615 +8849603 +8848893 +8849045 +8851007 +8851005 +8848963 +8848965 +8848975 +8848977 +8849065 +8851033 +8851031 +8851085 +8851121 +8851081 +8851035 +8851047 +8849503 +8849191 +8851067 +8851051 +8851083 +8849109 +8849111 +8849135 +8851061 +8849361 +8851123 +8849325 +8851115 +8851107 +8851105 +8851119 +8851131 +8851133 +8849611 +8849599 +8849773 +8849755 +8849743 +8851147 +8849585 +8849705 +8851075 +8851039 +8849171 +8851045 +8851103 +8850537 +8851151 +8849579 +8851145 +8849879 +8851157 +8849991 +8849927 +8851161 +8849977 +8850995 +8848925 +8849087 +8849049 +8849061 +8848981 +8849005 +8850697 +8849007 +8850695 +8848973 +8849041 +8849015 +8849017 +8849383 +8849319 +8849157 +8849197 +8850717 +8849195 +8849321 +8849449 +8850771 +8850539 +8849403 +8849333 +8849327 +8849181 +8849183 +8849329 +8849377 +8849379 +8850763 +8849369 +8849367 +8849363 +8849315 +8849287 +8849267 +8849247 +8849285 +8851073 +8849265 +8849193 +8849249 +8849229 +8849189 +8849133 +8849131 +8849163 +8849365 +8849495 +8849353 +8849331 +8849455 +8849417 +8849453 +8850543 +8849557 +8849591 +8849675 +8849697 +8849733 +8849635 +8849657 +8850815 +8849669 +8849569 +8849619 +8850809 +8849387 +8849341 +8849283 +8849317 +8850737 +8849239 +8850721 +8849251 +8849219 +8849217 +8850719 +8849209 +8851043 +8849259 +8849233 +8849231 +8849457 +8849477 +8849589 +8849663 +8849727 +8850825 +8849745 +8849747 +8850831 +8849707 +8849695 +8850817 +8850819 +8849797 +8849815 +8850851 +8849819 +8849979 +8850853 +8849941 +8849957 +8850855 +8849963 +8850743 +8850745 +8850751 +8850753 +8850755 +8850757 +8851219 +8850807 +8850811 +8850813 +8870928 +8870930 +8870926 +8871300 +8872476 +8872478 +8871374 +8871426 +8871424 +8871402 +8871362 +8871292 +8871302 +8872136 +8871560 +8871564 +8871558 +8871442 +8871444 +8871478 +8871484 +8871472 +8871568 +8871656 +8871650 +8871710 +8871754 +8871778 +8874554 +8871860 +8874580 +8874644 +8874670 +8875360 +8871132 +8871430 +8871528 +8871566 +8871602 +8871618 +8871646 +8871692 +8871726 +8871734 +8871788 +8871798 +8874536 +8871836 +8871812 +8874572 +8874594 +8874658 +8874682 +8875220 +8875256 +8875294 +8872124 +8871324 +8871332 +8871322 +8871250 +8871194 +8871550 +8871530 +8871518 +8871378 +8875296 +8874606 +8871256 +8871090 +8870992 +8871166 +8870994 +8870962 +8871074 +8872812 +8872814 +8871244 +8871064 +8870938 +8870998 +8871106 +8871114 +8872794 +8871082 +8872816 +8871416 +8871506 +8872832 +8871296 +8871284 +8871384 +8871200 +8872810 +8872828 +8872866 +8876016 +8871590 +8871662 +8871846 +8874568 +8874622 +8874646 +8874754 +8874856 +8874910 +8875002 +8875158 +8875188 +8875258 +8871774 +8871800 +8871856 +8871336 +8871258 +8871240 +8872118 +8871118 +8871238 +8871180 +8871168 +8871028 +8871030 +8871120 +8871290 +8871274 +8871314 +8871312 +8871232 +8871234 +8871104 +8871012 +8871018 +8871110 +8871196 +8871182 +8871172 +8871174 +8871164 +8871266 +8872130 +8871562 +8871422 +8871394 +8871410 +8871480 +8871466 +8871352 +8871276 +8871198 +8871278 +8871582 +8871648 +8871638 +8872864 +8874556 +8874548 +8875234 +8875400 +8875374 +8875380 +8875310 +8875290 +8875230 +8875830 +8875036 +8875068 +8874918 +8874784 +8874792 +8874632 +8871858 +8871854 +8872842 +8872846 +8871718 +8875172 +8871738 +8876022 +8876038 +8875030 +8875010 +8874814 +8874762 +8874690 +8874570 +8874656 +8874726 +8874828 +8875174 +8876028 +8875212 +8876042 +8875834 +8875268 +8875828 +8875058 +8875120 +8875042 +8875818 +8875814 +8874964 +8876018 +8875804 +8874758 +8872868 +8872818 +8871768 +8872860 +8872854 +8872856 +8876014 +8872870 +8874664 +8874712 +8874960 +8874894 +8876020 +8876024 +8871690 +8871452 +8871556 +8871100 +8872772 +8875326 +8875334 +8875338 +8875292 +8874868 +8874930 +8875032 +8874700 +8874720 +8871776 +8872504 +8871772 +8872872 +8871482 +8871520 +8871630 +8871712 +8871806 +8871852 +8874592 +8874648 +8874716 +8874772 +8874844 +8874864 +8874992 +8874728 +8874990 +8876130 +8871850 +8874924 +8875062 +8874922 +8875260 +8875162 +8875176 +8875198 +8875968 +8875970 +8875014 +8875824 +8875060 +8874736 +8875808 +8874786 +8872148 +8874652 +8871842 +8871790 +8872490 +8871338 +8871364 +8871628 +8871678 +8871704 +8871626 +8871654 +8871840 +8875890 +8874530 +8871810 +8876012 +8874538 +8874756 +8874798 +8874858 +8874812 +8874900 +8871792 +8871794 +8872970 +8871824 +8871736 +8871616 +8872844 +8871576 +8872840 +8871428 +8871388 +8871440 +8871470 +8872494 +8871508 +8871216 +8871034 +8871006 +8851177 +8850415 +8853139 +8850417 +8850603 +8850085 +8850337 +8850239 +8850225 +8850173 +8850179 +8850203 +8850315 +8851169 +8850601 +8850593 +8850083 +8850591 +8850589 +8850133 +8850113 +8850081 +8850587 +8850145 +8850191 +8850247 +8850259 +8848725 +8848727 +8848735 +8848709 +8848733 +8850653 +8853169 +8850463 +8853165 +8850445 +8853509 +8853447 +8850447 +8850433 +8850425 +8850407 +8850339 +8850313 +8850241 +8850219 +8850201 +8850177 +8850169 +8850861 +8850409 +8850411 +8850423 +8850903 +8850599 +8850375 +8850333 +8850303 +8850251 +8850233 +8850189 +8850127 +8850119 +8850115 +8850099 +8850335 +8850307 +8850277 +8850261 +8850253 +8850249 +8850231 +8850181 +8850035 +8850105 +8850157 +8850107 +8850045 +8850017 +8850015 +8850031 +8850039 +8850053 +8850109 +8850065 +8850033 +8850257 +8850287 +8850319 +8850357 +8850403 +8850383 +8851171 +8850435 +8853531 +8853193 +8850229 +8850183 +8850165 +8850149 +8850111 +8850101 +8850095 +8850067 +8850163 +8850153 +8850147 +8850049 +8850047 +8850037 +8850029 +8850097 +8850123 +8850263 +8850387 +8850389 +8850401 +8850419 +8853449 +8853189 +8853203 +8849791 +8849779 +8850567 +8849679 +8850555 +8849653 +8849643 +8849641 +8849613 +8849565 +8849561 +8849525 +8850531 +8849507 +8849489 +8849485 +8849483 +8849467 +8849445 +8849429 +8849433 +8849391 +8851125 +8849159 +8849207 +8849099 +8849067 +8849125 +8851015 +8849295 +8851049 +8851057 +8850495 +8851011 +8850991 +8849145 +8850493 +8850997 +8850983 +8848883 +8848849 +8850969 +8850973 +8850479 +8848765 +8850979 +8848901 +8848873 +8848945 +8849039 +8849059 +8850985 +8848993 +8848985 +8850989 +8850977 +8848771 +8848807 +8851191 +8848869 +8848943 +8850999 +8851009 +8849053 +8848917 +8851023 +8849167 +8851021 +8851027 +8851037 +8851041 +8851053 +8851079 +8851059 +8850527 +8850525 +8851077 +8850549 +8850551 +8849753 +8851111 +8851109 +8849651 +8850523 +8849639 +8849563 +8851095 +8851055 +8849299 +8851063 +8851091 +8849441 +8849273 +8849245 +8851087 +8851089 +8849225 +8850535 +8850533 +8849515 +8851141 +8849661 +8849551 +8851143 +8851139 +8851129 +8849671 +8849713 +8851135 +8850561 +8849771 +8849775 +8849799 +8849847 +8849893 +8851159 +8849989 +8849955 +8849915 +8849795 +8849983 +8849937 +8849949 +8850583 +8849921 +8849837 +8849919 +8849889 +8850027 +8850013 +8849523 +8849521 +8850741 +8850739 +8850547 +8850521 +8849493 +8850501 +8850499 +8849393 +8849343 +8849305 +8849221 +8849223 +8849187 +8849149 +8849123 +8849081 +8849085 +8849083 +8849055 +8849051 +8848923 +8848919 +8848891 +8849701 +8850503 +8849293 +8849185 +8849151 +8849137 +8849097 +8849139 +8849127 +8849415 +8849397 +8849399 +8849211 +8849077 +8850707 +8849095 +8849155 +8849179 +8850709 +8850711 +8849177 +8849147 +8849093 +8849057 +8849037 +8850489 +8848969 +8848991 +8848979 +8850681 +8850679 +8850491 +8848947 +8848885 +8848971 +8848933 +8850487 +8848847 +8848801 +8848799 +8848791 +8848779 +8850665 +8848787 +8850483 +8848773 +8848759 +8850485 +8848935 +8848929 +8849119 +8849003 +8848987 +8848941 +8848907 +8848897 +8848857 +8849001 +8848909 +8848825 +8848805 +8848797 +8848795 +8850669 +8848899 +8848867 +8849075 +8849029 +8849027 +8849079 +8849105 +8849103 +8849107 +8849153 +8849173 +8850713 +8849215 +8849237 +8850715 +8849269 +8849261 +8849281 +8849355 +8849381 +8849409 +8849411 +8849357 +8849443 +8849609 +8850781 +8849655 +8849659 +8849649 +8849647 +8850529 +8849359 +8849505 +8849487 +8849297 +8849481 +8849427 +8849275 +8849425 +8849389 +8849227 +8849531 +8849535 +8849553 +8849555 +8849575 +8849529 +8849693 +8849631 +8849689 +8850801 +8850803 +8849685 +8849691 +8849637 +8849781 +8849761 +8849725 +8850565 +8849769 +8849777 +8849793 +8849841 +8849885 +8849899 +8849935 +8850585 +8849965 +8850859 +8850857 +8849961 +8849719 +8849811 +8850841 +8849813 +8849801 +8849901 +8850569 +8849835 +8849911 +8849917 +8849845 +8849789 +8849869 +8849971 +8849999 +8848547 +8851193 +8848687 +8850961 +8850957 +8850959 +8850963 +8848827 +8848817 +8848587 +8848551 +8848525 +8848549 +8848573 +8848723 +8848729 +8848731 +8848741 +8848863 +8851185 +8851187 +8848861 +8848743 +8848639 +8851189 +8851175 +8848591 +8848513 +8848593 +8848515 +8848751 +8850877 +8850879 +8850889 +8850869 +8850871 +8850873 +8850749 +8850777 +8850779 +8850785 +8850789 +8850791 +8850845 +8850847 +8850849 +8898146 +8898142 +8897286 +8898148 +8898150 +8872436 +8872438 +8870890 +8872448 +8872440 +8872464 +8872454 +8872458 +8872468 +8872472 +8874940 +8874770 +8872470 +8871294 +8871328 +8872488 +8871492 +8871578 +8871826 +8870936 +8872752 +8870952 +8871010 +8872784 +8871138 +8871268 +8871606 +8872808 +8872416 +8870896 +8872442 +8871086 +8872450 +8872452 +8872456 +8871186 +8872460 +8872462 +8871540 +8871580 +8871676 +8872496 +8872498 +8872852 +8871320 +8874748 +8871802 +8874524 +8871740 +8871586 +8872474 +8871126 +8872964 +8870946 +8848445 +8872434 +8872954 +8872956 +8872418 +8872426 +8872420 +8872428 +8872414 +8872958 +8872406 +8872444 +8844739 +8844733 +8844727 +8844537 +8844743 +8844555 +8844557 +8844615 +8844617 +8844647 +8844681 +8848397 +8844699 +8848405 +8848407 +8848425 +8848617 +8848619 +8848601 +8848613 +8848597 +8844683 +8848423 +8844621 +8853231 +8850103 +8850069 +8850323 +8850309 +8850311 +8850135 +8850093 +8850079 +8850077 +8850141 +8850117 +8850155 +8850209 +8850245 +8850301 +8853281 +8850361 +8850363 +8851167 +8850393 +8853219 +8850451 +8853527 +8853191 +8853243 +8853457 +8853469 +8850367 +8853129 +8850329 +8850439 +8850343 +8850235 +8850285 +8850195 +8850205 +8850185 +8850159 +8850151 +8850171 +8850213 +8850217 +8850227 +8850269 +8850291 +8850275 +8850289 +8850327 +8853127 +8850395 +8850431 +8850457 +8850355 +8850353 +8850379 +8850461 +8848559 +8848561 +8848649 +8848647 +8848635 +8848631 +8848645 +8848637 +8848605 +8848623 +8844645 +8844669 +8844491 +8844737 +8844563 +8844521 +8844511 +8844507 +8844509 +8844515 +8844517 +8844535 +8844531 +8844547 +8844567 +8848667 +8848685 +8848717 +8844635 +8844679 +8844673 +8844695 +8851197 +8844543 +8844561 +8844595 +8844611 +8853451 +8850381 +8850893 +8850341 +8850295 +8850243 +8850207 +8850143 +8850267 +8850137 +8850125 +8850091 +8850325 +8850321 +8850089 +8850087 +8853481 +8853237 +8853483 +8853211 +8853505 +8850443 +8853503 +8853167 +8853161 +8850597 +8850365 +8850359 +8850385 +8850405 +8850899 +8850901 +8850897 +8853511 +8853479 +8853201 +8853179 +8853181 +8853205 +8853485 +8853241 +8853499 +8853227 +8853475 +8853473 +8853465 +8853497 +8853459 +8853207 +8853455 +8853153 +8850455 +8853141 +8850429 +8850391 +8853197 +8853171 +8853173 +8850453 +8853125 +8850441 +8850345 +8850331 +8850595 +8850283 +8850279 +8850237 +8850223 +8850215 +8850197 +8850199 +8850187 +8850175 +8850167 +8850281 +8850271 +8850273 +8853143 +8853163 +8853145 +8850459 +8850377 +8853147 +8853135 +8853507 +8853195 +8853535 +8853529 +8853533 +8853257 +8849303 +8851003 +8849271 +8851013 +8850993 +8849459 +8849463 +8851025 +8849501 +8849549 +8849623 +8849699 +8849807 +8849805 +8849825 +8849853 +8849709 +8851093 +8849737 +8851117 +8849849 +8849881 +8849909 +8851149 +8849401 +8851065 +8850511 +8849373 +8849313 +8851019 +8851029 +8850515 +8851071 +8849545 +8853467 +8853463 +8853223 +8853225 +8853229 +8853245 +8853251 +8853515 +8853517 +8849821 +8849839 +8849851 +8849867 +8849871 +8849873 +8849895 +8850579 +8850581 +8849907 +8849925 +8849903 +8849913 +8849897 +8849829 +8849823 +8849809 +8850573 +8849169 +8849213 +8849165 +8850685 +8849115 +8849121 +8850683 +8849587 +8849571 +8849547 +8850509 +8849465 +8849461 +8849817 +8849863 +8850571 +8849783 +8850799 +8849751 +8849735 +8849711 +8849785 +8849875 +8849861 +8849859 +8849831 +8849715 +8849595 +8850513 +8849471 +8849419 +8849617 +8851099 +8850723 +8850725 +8849533 +8849577 +8849395 +8849371 +8849311 +8849421 +8849717 +8849519 +8849517 +8849833 +8850805 +8849787 +8851137 +8849765 +8849667 +8851113 +8849023 +8849091 +8849025 +8851069 +8849439 +8849277 +8851017 +8849475 +8849499 +8851097 +8849865 +8849855 +8849767 +8849827 +8849741 +8849723 +8849739 +8849665 +8851101 +8849645 +8849073 +8849047 +8849513 +8850519 +8849509 +8850731 +8850733 +8849497 +8849473 +8850517 +8849469 +8849437 +8849279 +8849543 +8849721 +8849951 +8850001 +8850073 +8850007 +8850055 +8850059 +8850009 +8849959 +8850071 +8850063 +8850021 +8849985 +8851153 +8850003 +8849931 +8849943 +8849973 +8849945 +8850061 +8850023 +8850005 +8849997 +8848481 +8848495 +8850915 +8848465 +8848603 +8850911 +8848459 +8848521 +8850943 +8848749 +8848701 +8850933 +8848629 +8848505 +8850917 +8848473 +8850925 +8850921 +8850927 +8850937 +8850929 +8848503 +8848565 +8850941 +8850939 +8851225 +8850949 +8848703 +8848679 +8848715 +8848763 +8848767 +8850965 +8848913 +8848881 +8848921 +8848859 +8848837 +8850931 +8848463 +8848517 +8848477 +8850923 +8850935 +8850945 +8848663 +8850951 +8848719 +8849257 +8848785 +8850953 +8850955 +8848781 +8850971 +8849161 +8850967 +8848843 +8850975 +8850987 +8848957 +8848949 +8849021 +8848995 +8849011 +8849101 +8849043 +8849203 +8849035 +8851001 +8849201 +8849243 +8849063 +8849033 +8850981 +8850913 +8849923 +8849987 +8849995 +8850019 +8850043 +8849975 +8849993 +8850025 +8849929 +8849933 +8850839 +8850837 +8849887 +8849891 +8849947 +8849967 +8850843 +8850011 +8849969 +8848595 +8848571 +8848569 +8848483 +8848467 +8848541 +8848519 +8848461 +8848655 +8848675 +8848643 +8850649 +8848633 +8848615 +8848533 +8848531 +8848487 +8848489 +8850919 +8848507 +8848485 +8848493 +8850621 +8848491 +8848475 +8848599 +8848585 +8850639 +8848583 +8848563 +8848529 +8848501 +8850625 +8848509 +8848581 +8848527 +8848607 +8848625 +8848611 +8848575 +8848609 +8850637 +8848577 +8848545 +8848543 +8848511 +8851217 +8848669 +8848699 +8848737 +8851199 +8851173 +8848775 +8848777 +8848783 +8848803 +8848819 +8848769 +8848757 +8848841 +8850671 +8848853 +8848871 +8850673 +8850675 +8850481 +8850657 +8848579 +8848539 +8848537 +8848497 +8848479 +8848535 +8850623 +8848499 +8848621 +8848627 +8848671 +8848673 +8848691 +8848739 +8848875 +8848877 +8848895 +8848989 +8849013 +8849089 +8849175 +8848835 +8848815 +8848833 +8850651 +8848761 +8848813 +8848879 +8848905 +8848939 +8848953 +8848959 +8849069 +8849141 +8848903 +8848793 +8848821 +8850655 +8848937 +8848955 +8848967 +8849009 +8848961 +8848951 +8848927 +8849071 +8849129 +8849143 +8850677 +8849031 +8848983 +8848439 +8850909 +8848433 +8844609 +8844633 +8844687 +8844551 +8844499 +8844773 +8844519 +8844505 +8844475 +8844479 +8844513 +8844525 +8844533 +8844493 +8844779 +8844775 +8844523 +8844591 +8844781 +8844629 +8844783 +8844675 +8844637 +8844641 +8848403 +8848417 +8850907 +8848441 +8848451 +8848447 +8848421 +8844697 +8844693 +8848429 +8850905 +8848409 +8844667 +8844627 +8844483 +8844487 +8848411 +8848443 +8844665 +8844527 +8844501 +8844503 +8844481 +8844549 +8844789 +8844559 +8844545 +8844593 +8844613 +8844649 +8844659 +8844655 +8844651 +8844657 +8844661 +8844677 +8844653 +8844663 +8844701 +8848415 +8850611 +8848427 +8848437 +8844671 +8857721 +8848395 +8844691 +8848413 +8850607 +8848431 +8844689 +8844639 +8844607 +8844735 +8844485 +8844573 +8844603 +8844451 +8853247 +8844463 +8848657 +8844589 +8844729 +8844473 +8844771 +8844447 +8844453 +8844465 +8844461 +8844741 +8853453 +8853477 +8853471 +8853461 +8853253 +8853255 +8844467 +8850609 +8850613 +8850615 +8850617 +8850619 +8850627 +8850629 +8850631 +8850633 +8850635 +8850641 +8850643 +8850645 +8850647 +8850883 +8850885 +8850687 +8850689 +8850691 +8850699 +8850701 +8850705 +8850747 +8850759 +8850761 +8850773 +8850775 +8850783 +8850787 +8850793 +8850795 +8850797 +8850829 +8850833 +8850835 +8897282 +8897280 +8846823 +8846805 +8846811 +8846815 +8842809 +8846797 +8846801 +8846813 +8846809 +8842805 +8842803 +8842789 +8842793 +8842501 +8842783 +8842785 +8842475 +8842457 +8842433 +8842705 +8842405 +8842395 +8842787 +8845863 +8845857 +8845849 +8845837 +8845829 +8845809 +8845805 +8845739 +8845727 +8845719 +8845667 +8848721 +8845661 +8848707 +8848697 +8848693 +8848689 +8848661 +8848665 +8848653 +8842509 +8845483 +8845451 +8845523 +8842331 +8851731 +8851681 +8851693 +8847329 +8851683 +8846649 +8850075 +8846635 +8850131 +8850129 +8850139 +8847327 +8846415 +8846411 +8846413 +8850255 +8850293 +8850297 +8851165 +8850397 +8850347 +8851163 +8850427 +8853519 +8853155 +8853521 +8852061 +8851737 +8853199 +8853525 +8853523 +8845337 +8842603 +8845329 +8842583 +8842567 +8842533 +8842329 +8842333 +8842341 +8844597 +8844769 +8844767 +8844605 +8844623 +8844625 +8845413 +8846817 +8845443 +8845465 +8845469 +8845477 +8845435 +8846819 +8845449 +8845467 +8845505 +8844539 +8844553 +8844529 +8844489 +8844731 +8844477 +8844469 +8844457 +8852035 +8851715 +8851701 +8851697 +8851695 +8852047 +8852017 +8851687 +8853137 +8853131 +8853539 +8850437 +8850413 +8850399 +8850373 +8852063 +8846679 +8851685 +8846667 +8846669 +8847247 +8851679 +8846681 +8846645 +8846637 +8846615 +8846575 +8846501 +8847187 +8846493 +8846591 +8847205 +8850867 +8850221 +8850865 +8850211 +8850863 +8850161 +8846581 +8851213 +8846571 +8846573 +8846491 +8846417 +8846599 +8846617 +8851215 +8847225 +8847287 +8846647 +8847235 +8846633 +8850305 +8846627 +8857723 +8847233 +8850299 +8850875 +8852065 +8846683 +8847251 +8846659 +8847249 +8846653 +8850349 +8850371 +8850891 +8850351 +8850887 +8850369 +8850317 +8850265 +8850421 +8850895 +8853537 +8853133 +8853501 +8853151 +8853157 +8852019 +8852049 +8851703 +8851733 +8852055 +8851739 +8851709 +8851711 +8851713 +8851707 +8853221 +8853513 +8853235 +8853213 +8851705 +8853233 +8853215 +8853209 +8853183 +8853185 +8853217 +8851735 +8852059 +8851729 +8846665 +8846421 +8846337 +8846345 +8846449 +8846539 +8846643 +8846595 +8846577 +8846625 +8846167 +8846287 +8847321 +8846335 +8846309 +8845919 +8845957 +8845975 +8846029 +8845973 +8846045 +8846057 +8846067 +8846079 +8849253 +8846131 +8846745 +8846063 +8849323 +8847315 +8849113 +8845887 +8849205 +8849335 +8849435 +8850505 +8849633 +8849539 +8846105 +8846175 +8849683 +8850507 +8849541 +8849681 +8849731 +8849757 +8851727 +8852053 +8852051 +8852021 +8846583 +8846517 +8847183 +8846461 +8847333 +8846515 +8847179 +8847165 +8846375 +8846585 +8847199 +8846565 +8846537 +8847189 +8846519 +8847185 +8846495 +8847181 +8846541 +8847197 +8846545 +8847191 +8846629 +8846639 +8847229 +8846623 +8847223 +8846609 +8846605 +8847207 +8846603 +8846579 +8847201 +8847203 +8846631 +8847227 +8847289 +8847103 +8846225 +8846371 +8847155 +8846363 +8847147 +8847139 +8846285 +8845947 +8845937 +8847035 +8846041 +8846049 +8846011 +8845987 +8846097 +8846107 +8846113 +8846749 +8846133 +8846141 +8846179 +8846199 +8846201 +8846189 +8850577 +8846187 +8849857 +8849843 +8846095 +8846077 +8846109 +8847077 +8846129 +8846143 +8847099 +8846127 +8846091 +8846085 +8846747 +8846065 +8851201 +8851179 +8849339 +8849301 +8849291 +8849263 +8849289 +8850703 +8845967 +8849235 +8845963 +8845959 +8849199 +8845961 +8849117 +8845965 +8849255 +8849241 +8846087 +8849537 +8846185 +8846177 +8851203 +8849763 +8849749 +8849687 +8849673 +8849581 +8849605 +8849677 +8849939 +8850051 +8846339 +8849883 +8846319 +8846323 +8846249 +8846255 +8847323 +8846209 +8846183 +8846149 +8845573 +8847305 +8845757 +8845585 +8845649 +8847301 +8845615 +8847309 +8845841 +8845823 +8845827 +8847311 +8845709 +8845617 +8847303 +8845557 +8845607 +8845625 +8845599 +8845543 +8848567 +8848553 +8848523 +8848557 +8848555 +8848713 +8848747 +8848745 +8850947 +8848811 +8849019 +8848997 +8848999 +8848931 +8845845 +8845767 +8845819 +8845865 +8845867 +8845871 +8847313 +8845899 +8845437 +8845471 +8845531 +8845503 +8845397 +8845517 +8845515 +8845535 +8845479 +8845453 +8842589 +8848471 +8848399 +8848435 +8848449 +8845431 +8848469 +8846195 +8851183 +8846207 +8849877 +8851205 +8849953 +8851207 +8849981 +8851211 +8850575 +8851209 +8849905 +8846259 +8847141 +8846191 +8847277 +8847127 +8846169 +8846263 +8846155 +8846123 +8847089 +8846121 +8847081 +8845671 +8846969 +8845747 +8845651 +8845645 +8845637 +8845623 +8845621 +8845639 +8845673 +8845835 +8845785 +8847263 +8846987 +8845737 +8845717 +8845677 +8845679 +8845665 +8845605 +8846941 +8848705 +8848695 +8848641 +8848681 +8845549 +8845545 +8848659 +8848651 +8848589 +8848677 +8848683 +8848711 +8847331 +8845733 +8845735 +8848789 +8848809 +8848823 +8848831 +8848839 +8848845 +8848855 +8848865 +8850659 +8850661 +8848851 +8848889 +8848911 +8850663 +8850667 +8848915 +8848887 +8848829 +8845833 +8845815 +8847003 +8845885 +8845879 +8847269 +8847019 +8845495 +8842605 +8842807 +8842839 +8845343 +8845349 +8845383 +8848453 +8845385 +8845341 +8848419 +8845351 +8848455 +8848457 +8845395 +8845415 +8845411 +8845379 +8842573 +8844643 +8844631 +8844497 +8844449 +8844455 +8844777 +8842473 +8842443 +8842441 +8842383 +8842449 +8842299 +8842379 +8842371 +8842373 +8842431 +8842529 +8842411 +8842409 +8842485 +8842463 +8842611 +8842609 +8845335 +8842581 +8842835 +8845381 +8845389 +8842563 +8842795 +8844685 +8842565 +8851195 +8848401 +8842781 +8842779 +8844601 +8844599 +8844577 +8844759 +8844761 +8844495 +8844459 +8844471 +8844569 +8844787 +8844575 +8844581 +8844579 +8844541 +8844571 +8844753 +8844751 +8842471 +8844587 +8842769 +8842435 +8842437 +8842399 +8842403 +8842423 +8842305 +8842301 +8842297 +8842327 +8842313 +8842359 +8842375 +8842345 +8842757 +8842413 +8842459 +8842539 +8842797 +8842799 +8842801 +8842535 +8842493 +8842465 +8842461 +8842537 +8842513 +8842511 +8847335 +8846795 +8845353 +8846803 +8845357 +8846799 +8846807 +8842531 +8845339 +8842607 +8842791 +8842551 +8847339 +26326998 +8845377 +8846829 +8846833 +8848755 +8848753 +8847325 +8844565 +8845681 +8846477 +8847173 +8844755 +8844757 +8844585 +8842771 +8844763 +8842773 +8842777 +8842775 +8844765 +8845669 +8847341 +8852067 +8852069 +8846459 +8847345 +8847283 +8845321 +8842837 +8844583 +8847337 +8846837 +8846827 +8846825 +8846831 +8846835 +8846821 +8845373 +8842741 +8842743 +8842745 +8842747 +8842749 +8842751 +8842753 +8842763 +8842765 +8842767 +8844747 +8844749 +26326987 +26326991 +26326995 +8847209 +8847211 +8847213 +8847215 +8847221 +8850881 +8847151 +8847153 +8847157 +8850693 +8850727 +8850729 +8850735 +8850821 +8850823 +8850827 +166755084 +8830768 +8830792 +8845363 +8852037 +8852025 +8852039 +8847245 +8847237 +8847177 +8847161 +8847171 +8847143 +8847129 +8847123 +8847119 +8847097 +8847117 +8857725 +8847107 +8847109 +8847111 +8847095 +8846927 +8845627 +8846897 +8845583 +8845595 +8845439 +8845407 +8846729 +8846793 +8846929 +8846919 +8846889 +8846895 +8846907 +8846885 +8845513 +8847255 +8846879 +8845507 +8845491 +8845421 +8845417 +8845399 +8845365 +8845355 +8845331 +8842595 +8845323 +8842593 +8842561 +8842525 +8842479 +8842451 +8842427 +8842415 +8842387 +8842389 +8846881 +8846875 +8846849 +8845853 +8846731 +8845843 +8845839 +8845825 +8845781 +8845783 +8845753 +8845695 +8845729 +8845749 +8845799 +8845821 +8845461 +8842825 +8842357 +8846425 +8846773 +8846465 +8846547 +8842339 +8842323 +8842317 +8842309 +8842295 +8845559 +8846905 +8845533 +8845429 +8846845 +8847253 +8845427 +8846847 +8845403 +8845369 +8842601 +8845347 +8842585 +8842577 +8842569 +8842549 +8842499 +8842481 +8842393 +8842467 +8842761 +8842425 +8842421 +8842367 +8846365 +8846221 +8847343 +8846387 +8846439 +8846509 +8846507 +8846485 +8846511 +8846487 +8852041 +8852043 +8851699 +8851723 +8851719 +8851721 +8851717 +8851725 +8846651 +8846457 +8846173 +8846267 +8847319 +8846215 +8846111 +8846751 +8846081 +8846101 +8846073 +8846099 +8846147 +8846753 +8846755 +8846757 +8846071 +8845997 +8845955 +8846733 +8845927 +8845923 +8845921 +8846015 +8845935 +8846005 +8845991 +8846053 +8846125 +8846663 +8846677 +8852013 +8847241 +8847243 +8852027 +8847239 +8846661 +8847231 +8846607 +8847349 +8846601 +8846621 +8847163 +8847145 +8846349 +8847279 +8847281 +8846245 +8846329 +8847133 +8846233 +8847113 +8846237 +8846217 +8846219 +8846331 +8846357 +8847149 +8846321 +8846271 +8847135 +8847137 +8846273 +8846269 +8846257 +8847131 +8847125 +8846213 +8847115 +8846165 +8846153 +8846117 +8847073 +8846163 +8846115 +8846103 +8846083 +8846159 +8847105 +8847091 +8847093 +8846735 +8846037 +8846031 +8846047 +8846061 +8847061 +8845951 +8845913 +8846009 +8845983 +8845953 +8845933 +8845925 +8847021 +8847271 +8846033 +8846021 +8846051 +8846055 +8846019 +8846017 +8847049 +8846059 +8846741 +8847067 +8846119 +8846743 +8846139 +8847307 +8845711 +8845779 +8845675 +8847299 +8845555 +8845701 +8845877 +8845807 +8845769 +8845903 +8845859 +8845911 +8845873 +8845875 +8845901 +8845897 +8845609 +8845653 +8845561 +8847295 +8845553 +8845509 +8845811 +8845791 +8845789 +8846963 +8845697 +8845691 +8845633 +8845603 +8845631 +8845601 +8846925 +8845715 +8845751 +8845765 +8845851 +8845881 +8847005 +8845861 +8845869 +8847265 +8845883 +8847007 +8845801 +8846949 +8845619 +8846921 +8845629 +8846891 +8847257 +8845537 +8846887 +8845539 +8845493 +8846877 +8845485 +8846865 +8846863 +8846839 +8842307 +8842315 +8842397 +8842381 +8842827 +8842823 +8842815 +8842819 +8842321 +8842811 +8842813 +8842439 +8842571 +8842429 +8842495 +8842599 +8842575 +8842547 +8842543 +8842497 +8842455 +8842831 +8842523 +8842587 +8845371 +8847291 +8845433 +8845455 +8845487 +8845511 +8845489 +8845387 +8842407 +8842711 +8842707 +8842833 +8842829 +8842447 +8842517 +8842559 +8842477 +8842419 +8842351 +8842353 +8842385 +8842347 +8842817 +8842821 +8842401 +8842453 +8842527 +8845325 +8842597 +8845367 +8845401 +8845393 +8845541 +8845463 +8845529 +8845361 +8846725 +8845409 +8845425 +8845663 +8846761 +8846261 +8846369 +8846227 +8846301 +8846311 +8846399 +8846523 +8846613 +8852057 +8846641 +8846657 +8846569 +8846589 +8846593 +8846787 +8846553 +8846529 +8846467 +8846505 +8846785 +8846557 +8846535 +8842355 +8842365 +8842377 +8842363 +8842361 +8842755 +8842325 +8842335 +8842343 +8842337 +8842311 +8842293 +8842319 +8842505 +8842503 +8845319 +8845345 +8842553 +8842545 +8842469 +8842483 +8842579 +8845405 +8846853 +8846857 +8846873 +8842591 +8842557 +8842515 +8842489 +8842445 +8842759 +8842555 +8842541 +8842709 +8842487 +8842369 +8842349 +8842391 +8842417 +8845333 +8845419 +8845423 +8846893 +8845575 +8846915 +8845567 +8845565 +8845569 +8845571 +8846899 +8845563 +8845579 +8846909 +8845577 +8846901 +8845593 +8845581 +8845527 +8845525 +8846851 +8845375 +8846727 +8845327 +8846791 +8846789 +8845441 +8846841 +8846937 +8847259 +8846951 +8845699 +8846943 +8847101 +8846305 +8846393 +8847121 +8846483 +8846469 +8846395 +8846333 +8846355 +8846317 +8847169 +8846451 +8847167 +8847159 +8847175 +8846503 +8846559 +8847193 +8846619 +8847285 +8847217 +8846611 +8851677 +8846675 +8846655 +8852015 +8846671 +8846597 +8846587 +8846567 +8846563 +8846543 +8846531 +8846521 +8846561 +8846555 +8846533 +8846525 +8846769 +8846513 +8851689 +8831532 +8831866 +8851691 +8852033 +8846767 +8846551 +8852029 +8852031 +8852045 +8846871 +8846861 +8846855 +8846859 +8846917 +8846913 +8846911 +8846953 +8846947 +8847219 +8847195 +8847347 +8846939 +8846945 +8846903 +8846347 +8846775 +8851675 +8846673 +8846549 +8846527 +8846843 +8846931 +8846933 +8846935 +8831748 +8831736 +8831734 +8831730 +8831726 +8831710 +8831712 +8831706 +8831680 +8831666 +8831672 +8831692 +8831894 +8831642 +8831714 +8830900 +8830796 +8831896 +8831512 +8831054 +8831746 +8831048 +8831582 +8830988 +8831542 +8830776 +8830890 +8830834 +8830806 +8830804 +8831862 +8830808 +8830772 +8830780 +8830778 +8830756 +8831502 +8830794 +8830788 +8830800 +8830810 +8830836 +8830830 +8830886 +8830884 +8830846 +8830894 +8830920 +8830990 +8830980 +8830998 +8831584 +8831536 +8831506 +8830764 +8831516 +8830758 +8831518 +8831044 +8831602 +8831742 +8831740 +8831738 +8831744 +8830896 +8831722 +8831720 +8830892 +8831718 +8831716 +8831694 +8830832 +8830824 +8831690 +8830820 +8831688 +8831696 +8830822 +8830818 +8831676 +8830814 +8830816 +8831684 +8831674 +8830812 +8830826 +8830828 +8831700 +8831698 +8831682 +8830774 +8830790 +8831660 +8830762 +8831650 +8831640 +8831504 +8830766 +8831644 +8831646 +8830786 +8830784 +8831656 +8831654 +8831664 +8831670 +8830840 +8830838 +8830842 +8831898 +8831704 +8831702 +8831708 +8830902 +8831724 +8830878 +8830874 +8830870 +8830858 +8830872 +8830940 +8831728 +8830928 +8830916 +8830926 +8831732 +8830932 +8831548 +8830944 +8830936 +8830938 +8830934 +8830946 +8831598 +8830760 +8831514 +8831648 +8830770 +8831546 +8830922 +8831890 +8831892 +8831668 +8831678 +8831754 +8831658 +8831652 +8831510 +26327789 +8831884 +8831886 +8831888 +8810275 +8810177 +8810181 +26329835 +9126966 +9127834 +8831608 +8831756 +8847085 +8847071 +8847063 +8847041 +8847037 +8847029 +8847025 +8847013 +8847017 +8846985 +8846981 +8846961 +8846973 +8846971 +8846967 +8846959 +8845817 +8846975 +8847261 +8845795 +8845855 +8847293 +8845707 +8846955 +8845703 +8845659 +8845587 +8845501 +8845547 +933030018 +8845969 +8845971 +8846027 +8845909 +8845943 +8845945 +8845889 +8845907 +8846039 +8847043 +8846025 +8846035 +8846007 +8845995 +8846023 +8845981 +8845993 +8847031 +8845985 +8845949 +8845915 +8845905 +8846043 +8846001 +8845999 +8845793 +8845813 +8846013 +8847039 +8845917 +8846997 +8847001 +8845979 +8846991 +8845797 +8846979 +8846977 +8845445 +8845391 +8845457 +8845475 +8845499 +8845481 +8845447 +8845705 +8845611 +8845635 +8845613 +8845551 +8845519 +8845643 +8845803 +8845597 +8845763 +8847297 +8845655 +8845693 +8845777 +8845895 +8845977 +8845989 +8846737 +8847317 +8846193 +8846161 +8846135 +8846243 +8845591 +8845497 +8845473 +8845459 +8845657 +8845641 +8845647 +8846923 +8845521 +8845589 +8846867 +8846869 +8846983 +8845847 +8845787 +8845775 +8846965 +8845759 +8845745 +8845721 +8845685 +8845761 +8845741 +8845687 +8846883 +8845689 +8845683 +8845723 +8845931 +8847009 +8845929 +8847267 +8845941 +8847015 +8847023 +8846003 +8847033 +8847027 +8847051 +8847273 +8846093 +8846075 +8847059 +8847055 +8847275 +8846137 +8847069 +8846145 +8846151 +8846171 +8847079 +8846157 +8847075 +8846181 +8847087 +8847083 +8846343 +8846069 +933030019 +8830798 +8831564 +8831864 +8831508 +8831190 +8831860 +8831606 +8846957 +8846989 +8847045 +8847057 +8847053 +8846995 +8846993 +8846999 +8847011 +8846089 +8847065 +8847047 +8831612 +8831058 +8831050 +8831042 +8831038 +8831034 +8831032 +8831026 +8831020 +8830994 +8830992 +8830982 +8830976 +8830942 +8831552 +8830930 +8831544 +8830906 +8830888 +8830880 +8830866 +8830864 +8830856 +8830854 +8831522 +8831662 +8831798 +8831808 +8831792 +8831096 +8831786 +8831072 +8831604 +8831046 +8831592 +8831576 +8831570 +8831566 +8831562 +8831794 +8831780 +8831060 +8831750 +8831580 +8830974 +8830960 +8830956 +26327848 +8831534 +8831176 +8831174 +8830850 +8830954 +8831588 +8831168 +8831152 +8831624 +8831150 +8831158 +8831170 +8831204 +8831126 +8831128 +8831146 +8831872 +8831084 +8831068 +8831870 +8831080 +8831102 +8831560 +8830924 +8830782 +8831868 +8831596 +8831586 +8831568 +8830986 +8831074 +8831094 +8831620 +8831110 +8831550 +8831528 +8830844 +8831524 +8831540 +8831880 +8831526 +8830978 +8831172 +8831166 +8831156 +8831154 +8831132 +8831812 +8831138 +8831134 +8831136 +8831088 +8831782 +8831778 +8831090 +8831092 +8831082 +8831788 +8831770 +8831774 +8831070 +8831776 +8831086 +8831772 +8831078 +8831066 +8831558 +8831556 +8831610 +8831600 +8831594 +8831018 +8831014 +8831008 +8831578 +8831572 +8831590 +8831006 +8831016 +8831098 +8831790 +8831796 +8831530 +8831878 +8830862 +8830860 +8830868 +8830882 +8830904 +8830910 +8830908 +8830914 +8830962 +8830972 +8830984 +8830996 +8831036 +8831040 +8831028 +8831022 +8831030 +8831024 +8831010 +8831052 +8831520 +8830852 +8830948 +8830950 +8831574 +8831004 +8831752 +8831002 +8830848 +8831000 +8831686 +8830964 +8831882 +26327794 +8831766 +8831768 +8831784 +8811137 +8810169 +8815929 +8812169 +8811819 +8812589 +8811211 +8811843 +8812403 +8812349 +8812323 +8812251 +8812207 +8812205 +8814309 +8814295 +8814281 +8814273 +8814217 +8815901 +8814195 +8814193 +8815903 +8814097 +8814061 +8814027 +8814019 +8812725 +8815869 +8812561 +8812537 +8813039 +8812259 +8812961 +8812821 +8811431 +8811403 +8812811 +8811363 +8811357 +8811333 +8811327 +8811309 +8811307 +8811299 +8811293 +8811165 +8811157 +8812815 +8812805 +8811355 +8811345 +8811323 +8811303 +8811215 +8811191 +8811189 +8811185 +8811181 +8811179 +8811161 +8811147 +8811145 +8811133 +8811127 +8811129 +8811113 +8811105 +8811103 +8811097 +8813077 +8811091 +8811089 +8811085 +8811077 +8810175 +8815999 +8814447 +8813033 +8812461 +8813169 +8812395 +8812379 +8812073 +8812919 +8812031 +8812025 +8811831 +8812723 +8812701 +8812689 +8812665 +8812809 +8811397 +8811295 +8813101 +8811283 +8811277 +8811245 +8811239 +8812827 +8811533 +8811499 +8813123 +8811173 +8812911 +8812203 +8812209 +8812337 +8812413 +8812489 +8812697 +8812687 +8812591 +8812695 +8816329 +8813049 +8813037 +8812947 +8812927 +8812061 +8811873 +8812001 +8811443 +8813199 +8811155 +8811135 +8811197 +8811195 +8811217 +8811243 +8811329 +8811365 +8812813 +8811353 +8811301 +8813195 +8813193 +8811169 +8813189 +8811153 +8811143 +8811121 +8811099 +8811117 +8811101 +8811095 +8811073 +8811083 +26329845 +8810179 +8811107 +8811115 +8813191 +8811149 +8811289 +8811325 +8812807 +8812819 +8811671 +8811689 +8812159 +8812993 +8812941 +8812381 +8812563 +8812485 +8812715 +8812617 +8812713 +8813985 +8815893 +8815911 +8814411 +8814507 +8816011 +8812071 +8811551 +8811697 +8811549 +8811541 +8813131 +8811455 +8813127 +8811501 +8813125 +8811367 +8812801 +8813111 +8811237 +8811225 +8813091 +8813179 +8813093 +8811247 +8811275 +8811267 +8811311 +8811233 +8811187 +8811177 +8811159 +8811151 +8811139 +8813081 +8811125 +8811111 +8813187 +8811123 +8811119 +8813079 +8811087 +8813069 +8813177 +8810173 +8810171 +8811079 +8811075 +8811141 +8811193 +8812963 +8813151 +8812959 +8812467 +8814015 +8814017 +8814033 +8814037 +8814047 +8814051 +8814041 +8814031 +8814025 +8812727 +8815873 +8812719 +8812705 +8812729 +8813959 +8812703 +8814117 +8814045 +8814035 +8814029 +8814049 +8814091 +8814093 +8814099 +8814243 +8814255 +8814259 +8814265 +8814263 +8814249 +8814215 +8814211 +8814213 +8814201 +8814271 +8814307 +8814311 +8814323 +8814321 +8814317 +8814305 +8814287 +8814207 +8814231 +8814237 +8814245 +8814247 +8814229 +8814257 +8814253 +8814203 +8814199 +8814221 +8814275 +8814285 +8814313 +8816001 +8816295 +8811845 +8812017 +8810163 +8810167 +26329847 +8811361 +8811765 +8813957 +8811901 +8814327 +8811959 +26329839 +8810165 +8811081 +8811109 +8811571 +8811565 +8811235 +8811347 +8814329 +8814343 +8811167 +8811175 +8811567 +8812829 +8812837 +8811589 +8811609 +8811597 +8811629 +8811633 +8811639 +8811651 +8811679 +8811623 +8811691 +8811707 +8811709 +8811777 +8811793 +8811807 +8812883 +8811835 +8811849 +8812893 +8811869 +8811921 +8811925 +8811907 +8811937 +8811939 +8814075 +8814341 +8811711 +8811903 +8812825 +8811287 +8811171 +8811183 +8813109 +8811547 +8812831 +8812833 +8811561 +8811545 +8811573 +8811611 +8811601 +8812863 +8811693 +8811919 +8811923 +8814077 +11574802 +11574804 +11574806 +8811719 +8811727 +8811737 +8811767 +26329631 +26329629 +11574820 +11574822 +11574826 +11574824 +11574818 +11574828 +11574910 +11574914 +11574920 +11574928 +11574932 +11574918 +11574922 +11574926 +11574912 +11574988 +11574990 +11574916 +11574924 +11574930 +11574934 +11574908 +8811987 +8811985 +8811967 +8811963 +8812067 +8812029 +8812027 +8812011 +8812005 +8812075 +8812129 +8812097 +8812935 +8812083 +8811961 +8811945 +11574904 +11574906 +11574900 +11574896 +11574892 +11574902 +11574898 +11574986 +11574984 +11574894 +11574890 +11574888 +11574884 +11574876 +11574872 +11574868 +11574866 +11574862 +11574858 +11574982 +11574856 +11574854 +11574860 +11574864 +11574886 +11574878 +11574882 +11574880 +11574874 +11574870 +11574852 +11574980 +11574846 +11574850 +11574848 +11574836 +11574840 +11574844 +11574842 +11574976 +11574978 +11574838 +11574832 +11574834 +11574830 +11574816 +11574812 +11574810 +11574814 +11574808 +26329841 +8810281 +8810283 +8810285 +8810287 +8810289 +8813067 +8813117 +8813095 +8813097 +8813099 +8813071 +8813073 +8813075 +8791415 +8831830 +8831832 +8831124 +8831804 +8831118 +8831116 +8831108 +8831104 +8831100 +8831616 +8830970 +8831806 +8831810 +8831628 +8831834 +8831836 +8831196 +8831186 +8831188 +8831180 +8830952 +8830958 +8831194 +8831182 +8831178 +8831622 +8831112 +8831554 +8831114 +8831120 +8831140 +8831164 +8831626 +8831184 +8831160 +8831828 +8831148 +8831822 +8831130 +8831800 +8831802 +8830968 +8831614 +8831106 +8831816 +8831814 +8831824 +8831192 +8831838 +8831840 +8831618 +8831764 +8831758 +8831762 +8831760 +8831818 +8831820 +8831064 +8811391 +8812949 +8811429 +8831826 +8830966 +8811373 +8811409 +8811453 +8814719 +8816039 +8816021 +8816003 +8814577 +8814553 +8814463 +8815937 +8814353 +8815915 +8814123 +8813991 +8813923 +8812699 +8812677 +8813163 +8812553 +8812469 +8815895 +8815891 +8815859 +8815843 +8813937 +8813939 +8812717 +8812709 +8812549 +8812521 +8812415 +8812269 +8812171 +8812957 +8811977 +8811909 +8811875 +8812885 +8811769 +8811751 +8811713 +8813129 +8811569 +8811555 +8811467 +8811563 +8812139 +8816273 +8812797 +8811521 +8816147 +8811911 +8812967 +8812543 +8811427 +8811459 +8812047 +8816019 +8815133 +8815123 +8815125 +8815119 +8815121 +8816165 +8816161 +8816151 +8815007 +8814973 +8814969 +8814911 +8814737 +8814699 +8814691 +8815995 +8814617 +8814599 +8814549 +8814537 +8814531 +8814525 +8814361 +8814357 +8814349 +8814315 +8814267 +8814239 +8814233 +8814197 +8814173 +8814161 +8815871 +8815839 +8816365 +8812721 +8813941 +8812707 +8812585 +8812439 +8812423 +8816261 +8814535 +8815955 +8815951 +8815949 +8814479 +8814481 +8815943 +8814449 +8814429 +8814387 +8814373 +8814371 +8814355 +8811297 +8811279 +8811263 +8811259 +8812773 +8813085 +8811221 +8811203 +8811201 +8814693 +8816025 +8816009 +8815463 +8815461 +8815459 +8815449 +8815435 +8815423 +8815421 +8815413 +8815409 +8815405 +8815387 +8815385 +8815363 +8815355 +8815347 +8815325 +8815321 +8815307 +8816213 +8815251 +8815249 +8815227 +8815191 +8811331 +8813103 +8811317 +8811321 +8811343 +8811341 +8812901 +8811745 +8811729 +8811723 +8813139 +8811687 +8816153 +8815105 +8815113 +8816169 +8815089 +8813943 +8811999 +8812997 +8813153 +8812999 +8812693 +8812593 +8812573 +8811401 +8811411 +8813121 +8811445 +8811457 +8812931 +8814725 +8814751 +8814767 +8814771 +8814775 +8814789 +8814809 +8814807 +8814805 +8814811 +8814817 +8814837 +8814839 +8814845 +8814847 +8814851 +8814853 +8814855 +8814863 +8814861 +8814865 +8814869 +8814881 +8814883 +8814887 +8814885 +8814891 +8814917 +8814959 +8814957 +8814961 +8814981 +8814987 +8815019 +8815037 +8815041 +8815045 +8815057 +8815055 +8815063 +8815079 +8816221 +8816207 +8816215 +8816355 +8811387 +8811449 +8811669 +8812891 +8812933 +8812033 +8812123 +8815193 +8815247 +8815305 +8815317 +8815315 +8815323 +8815349 +8815365 +8815407 +8815401 +8815395 +8815391 +8815393 +8815403 +8815437 +8815451 +8815425 +8815427 +8816241 +8815465 +8812583 +8812579 +8813993 +8816037 +8814675 +8815997 +8815945 +8815935 +8815919 +8814119 +8814095 +8812691 +8813059 +8812273 +8812459 +8813925 +8815885 +166755106 +8813933 +8812631 +8812515 +8813019 +8812951 +8812969 +8812041 +8812925 +8812923 +8811791 +8811579 +8811747 +8811619 +8812835 +8812881 +8811743 +8812839 +8811595 +8811627 +8811705 +8812955 +8812965 +8813047 +8815979 +8815983 +8816063 +8816097 +8816027 +8816005 +8814433 +8815967 +8815889 +8815853 +8812711 +8812437 +8812421 +8812991 +8812145 +8812587 +8811717 +8811517 +8811471 +8811383 +8811399 +8812799 +8812783 +8812777 +8812775 +8811315 +8811305 +8813197 +8811273 +8812771 +8811199 +8811131 +8811253 +8811229 +8811291 +8811265 +8811271 +8814701 +8814707 +8816029 +8816065 +8816149 +8816163 +8816117 +8816043 +8814121 +8823535 +8813989 +8813159 +166755105 +8812657 +8813061 +8813175 +8813173 +8813045 +8813023 +8811981 +8812899 +8813145 +8812907 +8812037 +8813135 +8812861 +8811731 +8811703 +8811625 +8815851 +8814967 +8815939 +8815857 +8815879 +8814235 +8814241 +8814223 +8814269 +8814335 +8814333 +8814365 +8814375 +8814385 +8814383 +8814379 +8814351 +8814359 +8814363 +8811395 +8811335 +8811319 +8811285 +8811163 +8811205 +8813083 +8811207 +8811251 +8811261 +8813089 +8811227 +8811249 +8813087 +8814393 +8814399 +8814391 +8814397 +8814441 +8814437 +8814425 +8814389 +8814431 +8814451 +8814465 +8814473 +8814511 +8814521 +8814515 +8814469 +8814499 +8814493 +8814509 +8814471 +8814485 +8814501 +8814529 +8815985 +8815987 +8815989 +8814635 +8814645 +8814637 +8814625 +8814621 +8814593 +8814591 +8814539 +8814583 +8814587 +8815969 +8814551 +8814601 +8814619 +8814639 +8816017 +8816047 +8814727 +8814721 +8816041 +8814723 +8814749 +8814743 +8814747 +8814741 +8814913 +8814975 +8815017 +8815099 +8816181 +8816173 +8816183 +8815141 +8816353 +8812547 +8813041 +8812603 +8811369 +8811593 +8816317 +8816051 +8812445 +8811859 +8815085 +8816135 +8815101 +8815131 +8815135 +8816197 +8815143 +8814377 +8814417 +8814439 +8814403 +8814443 +8814517 +8814651 +8813205 +8813211 +8815961 +8814459 +8814423 +8814419 +8814519 +8814653 +8814581 +8814545 +8814413 +8814405 +8814657 +8814761 +8812929 +8813947 +8813977 +8815927 +8814461 +8814475 +8815959 +8814541 +8814543 +8814585 +8814677 +8814641 +8814655 +8816033 +8814733 +8814745 +8814763 +8814787 +8814759 +8814755 +8814757 +8814753 +8814791 +8814799 +8814773 +8814777 +8814785 +8814819 +8814821 +8814835 +8814827 +8814831 +8814829 +8814841 +8814833 +8816067 +8814871 +8814877 +8816069 +8814849 +8814857 +8814873 +8814893 +8816083 +8814907 +8814921 +8814919 +8814915 +8814953 +8814965 +8814979 +8814983 +8816101 +8814963 +8814971 +8814989 +8815009 +8815029 +8815025 +8815027 +8815033 +8815039 +8815049 +8815047 +8815065 +8815071 +8816129 +8816127 +8815075 +8815083 +8814673 +8812939 +8816145 +8816125 +8815021 +8815015 +8814995 +8814927 +8814923 +8814879 +8814859 +8814825 +8814813 +8816045 +8816015 +8815931 +8814009 +8816263 +8814457 +8814347 +8815069 +8815845 +8814993 +8814497 +8814575 +8814643 +8815975 +8814605 +8814815 +8814843 +8814867 +8814895 +8816087 +8815031 +8815067 +8815077 +8816137 +8816133 +8813155 +8812471 +8813157 +8811715 +8813133 +8812329 +8812283 +8813137 +8814909 +8813165 +8813167 +8813171 +8813113 +8813115 +8791083 +8791133 +8816187 +8816179 +8816175 +8815129 +8815115 +8816159 +8816143 +8815073 +8816115 +8816271 +8814931 +8816089 +8816085 +8816075 +8814901 +8814797 +8815087 +8816131 +8816121 +8815053 +8815043 +8815011 +8816095 +8814943 +8816093 +8814823 +8816057 +8816035 +8814667 +8814663 +8815991 +8814567 +8814565 +8814547 +8815957 +8814503 +8815947 +8814445 +8814407 +8814191 +8814179 +8814169 +8814127 +8814083 +8814065 +8814057 +8813961 +8812633 +8812653 +8812595 +8812533 +8813043 +8813029 +8812473 +8812457 +8812443 +8812429 +8812397 +8812389 +8813017 +8812345 +8812319 +8812315 +8812311 +8812299 +8812275 +8812995 +8812241 +8812231 +8812215 +8812211 +8812177 +8812165 +8812141 +8812135 +8812945 +8812099 +8812081 +8812079 +8812039 +8811969 +8811957 +8811953 +8811941 +8811913 +8811893 +8811891 +8811879 +8811855 +8811839 +8811833 +8811783 +8811757 +8812873 +8811667 +8811643 +8812849 +8812845 +8812843 +8811605 +8811559 +8811477 +8811475 +8811441 +8811433 +8811417 +8811371 +8811419 +8811415 +8811393 +8811385 +8813107 +8811377 +8811479 +8813119 +8811469 +8811463 +8811461 +8816265 +8815447 +8816237 +8815439 +8815429 +8815419 +8815399 +8815379 +8815373 +8815375 +8815359 +8815351 +8815319 +8815309 +8815297 +8815291 +8815285 +8815259 +8815253 +8815239 +8815229 +8815221 +8815217 +8815203 +8815205 +8816361 +8812903 +8812373 +8815149 +8812105 +8811675 +8816335 +8813999 +8811451 +8812613 +8815831 +8816235 +8815199 +8815197 +8815195 +8815189 +8815181 +8815155 +8816193 +8816189 +8815377 +8816275 +8815485 +8815471 +8816245 +8812977 +8812187 +8812117 +8812063 +8812009 +8812407 +8815139 +8815163 +8812219 +8812155 +8811841 +8811823 +8811755 +8811695 +8811975 +8812915 +8812007 +8816007 +8814631 +8816291 +8814557 +8816287 +8814505 +8814023 +8816277 +8811557 +8811537 +8811531 +8811525 +8811511 +8811505 +8811497 +8811493 +8811489 +8811483 +8811485 +8811487 +8812627 +8813055 +8812607 +8812565 +8813161 +8811581 +8811997 +8812667 +8812671 +8812675 +8812673 +8812681 +8813057 +8812645 +8812193 +8812217 +8813929 +8813949 +8813955 +8813953 +8813967 +8813969 +8813975 +8813983 +8814007 +8814053 +8814367 +8816157 +8816139 +8815081 +8816077 +8814903 +8814937 +8811763 +8811677 +8811775 +8811895 +8812015 +8812221 +8812233 +8812359 +8812357 +8814135 +8816315 +8814609 +8814649 +8814703 +8814381 +8816331 +8816337 +8814671 +8816341 +8814729 +8814731 +8814899 +8816071 +8816079 +8816347 +8816091 +8815013 +8816349 +8814717 +8816013 +8814629 +8815981 +8814495 +8815897 +8816323 +8816319 +8815863 +8812539 +8812605 +8812503 +8813021 +8813001 +8812185 +8812115 +8812059 +8811933 +8812143 +8812897 +8812913 +8811753 +8811797 +8812869 +8811637 +8811735 +8811503 +8811577 +8811407 +8811473 +8811439 +8812779 +8812781 +8811349 +8811351 +8811337 +8811339 +8812789 +8812791 +8811405 +8811423 +8812817 +8812851 +8812173 +8813201 +8812383 +8812281 +8815481 +8815483 +8815491 +8815489 +8815475 +8816141 +8816059 +8812363 +8814079 +8814081 +8814683 +8814679 +8814665 +8814623 +8816289 +8814555 +8814395 +8814627 +8814669 +8814689 +8816055 +8816073 +8816081 +8815051 +8816107 +8816061 +8815875 +8814189 +8814141 +8812625 +8812501 +8813143 +8812077 +8813181 +8812119 +8813141 +8812895 +8811759 +8811761 +8811539 +8811481 +8811447 +8811491 +8811495 +8811509 +8811507 +8811513 +8811535 +8811413 +8811379 +8812785 +8812787 +8811381 +8813105 +8811435 +8823533 +8811607 +8812841 +8812847 +8811645 +8811641 +8812871 +8812875 +8811857 +8811853 +8811847 +8811943 +8811955 +8811915 +8811899 +8811889 +8811885 +8811897 +8811887 +8811917 +8812013 +8812051 +8812091 +8812103 +8812111 +8812127 +8812131 +8812133 +8812953 +8812157 +8812175 +8812227 +8812229 +8812987 +8812267 +8812289 +8812297 +8812333 +8812343 +8812347 +8813011 +8812365 +8813015 +8812353 +8812989 +8812361 +8812369 +8812411 +8812417 +8812449 +8812419 +8812447 +8812483 +8812493 +8812507 +8813035 +8812545 +8813025 +8812385 +8812327 +8812557 +8812569 +8812531 +8812527 +8812517 +8812555 +8812519 +8812505 +8812475 +8812465 +8812455 +8812477 +8812491 +8812481 +8812409 +8812391 +8812387 +8812375 +8812377 +8812401 +8812393 +8812371 +8812341 +8812287 +8812307 +8812305 +8812303 +8812301 +8812295 +8812279 +8812261 +8812271 +8812285 +8812263 +8812247 +8812239 +8812245 +8812291 +8812265 +8813987 +8814089 +8814069 +8816325 +8815921 +8814415 +8815941 +8815953 +8815973 +8815977 +8816339 +8816023 +8816343 +8814955 +8814997 +8814991 +8816103 +8816105 +8816119 +8816123 +8815091 +8815095 +8816351 +8815111 +8816177 +8816195 +8816201 +8816223 +8815397 +8815371 +8815353 +8815293 +8815261 +8815237 +8815185 +8815177 +8812237 +8812181 +8812189 +8812149 +8812163 +8812161 +8812147 +8812125 +8812107 +8812109 +8812049 +8812045 +8812019 +8812023 +8812043 +8812087 +8812101 +8812113 +8812121 +8812137 +8812167 +8812179 +8812223 +8812235 +8812257 +8812277 +8812293 +8812313 +8812317 +8812321 +8812351 +8812367 +8812399 +8812431 +8812453 +8812463 +8812495 +8812523 +8812535 +8812597 +8812623 +8812615 +8812639 +26329653 +8812685 +8812641 +8813053 +8812637 +8813963 +8815867 +8814073 +8814085 +8814105 +8814139 +8814153 +8814155 +8814163 +8814205 +8814219 +8814227 +8814283 +8815881 +8815907 +8814209 +8814225 +8814167 +8814165 +8814159 +8814157 +8814143 +8814125 +8814113 +8814111 +8814145 +8814147 +8814151 +8814115 +8814109 +8814129 +8814171 +8814181 +8815925 +8816285 +8815993 +8814945 +8816171 +8815117 +8815109 +8816297 +8815263 +8815187 +8815161 +8815183 +8815225 +8815235 +8815245 +8815289 +8815271 +8815313 +8816219 +8815383 +8816231 +8816233 +8815445 +8815479 +8815003 +8815153 +8815157 +8815201 +8815209 +8815207 +8815211 +8815213 +8815215 +8815233 +8815241 +8815219 +8815231 +8815273 +8815277 +8815279 +8815283 +8815281 +8815287 +8815299 +8815303 +8815301 +8815311 +8815361 +8816307 +8815381 +8811993 +8812575 +8814297 +8812601 +8812331 +8812191 +8812577 +8812089 +8811877 +8811741 +8812683 +8812669 +8812679 +8813183 +8814303 +8814299 +8813931 +8815837 +8813951 +8812497 +8815841 +8812663 +8812867 +8811733 +8811749 +8811861 +8811983 +8812021 +8812971 +8812195 +8812975 +8812225 +8812487 +8812571 +8812599 +8812629 +8812649 +8812659 +8813945 +8813965 +8813973 +8813981 +8814003 +8814011 +8814005 +8814021 +8814055 +8814289 +8814331 +8814369 +8816111 +8813919 +8814301 +8814043 +8814001 +8813185 +8812661 +8812611 +8812197 +8811995 +8811721 +8812647 +8812655 +8812889 +8811739 +8811653 +8811991 +8812981 +8812567 +8812499 +8813031 +8812213 +8814059 +8813979 +8814291 +8813203 +8816279 +8816281 +8816283 +26329663 +166755103 +166755104 +8791459 +8815265 +8814103 +8811621 +8811389 +8815495 +8815457 +8815455 +8815441 +8815369 +8815345 +8815335 +8816247 +8816239 +8816229 +8816225 +8815333 +8816217 +8816209 +8816203 +8815165 +8815167 +8815169 +8815171 +8815145 +8814739 +8816053 +8816293 +8816267 +8814713 +8814709 +8814705 +8814685 +8814661 +8814615 +8814589 +8814569 +8814561 +8815965 +8814533 +8814421 +8814325 +8815905 +8814183 +8814149 +8814613 +8814597 +8814523 +8814455 +8814409 +8814339 +8814279 +8815913 +8814137 +8815865 +8813997 +8815849 +8813935 +8812651 +8813921 +8812581 +8812525 +8812355 +8812325 +8812983 +8812943 +8812887 +8812879 +8811701 +8816243 +8816155 +8815223 +8812151 +8814659 +8812183 +8812451 +8815389 +8815295 +8813051 +8812513 +8812509 +8816299 +8815127 +8816167 +8815097 +8815173 +8813009 +8812339 +8813003 +8812255 +8811927 +8811883 +8811867 +8816211 +8813007 +8812243 +8812201 +8815963 +8814513 +8814491 +8814489 +8815877 +8814067 +8814063 +8813027 +8812433 +8812441 +8812427 +8812635 +8815833 +8816363 +8812551 +8812529 +8815443 +8815433 +8815417 +8816205 +8815339 +8815327 +8815243 +8816199 +8815151 +8815147 +8816185 +8815107 +8815137 +8815059 +8815005 +8816113 +8816099 +8814951 +8816345 +8814735 +8814711 +8814695 +8814633 +8815971 +8814611 +8814563 +8814477 +8814573 +8814483 +8814293 +8814251 +8812619 +8814131 +8814133 +8814087 +8814107 +8816313 +8814319 +8814571 +8814579 +8814427 +8816321 +8814277 +8815887 +8813971 +8814013 +8812621 +8812609 +8812541 +8812479 +8812425 +8812435 +8812249 +8812199 +8812153 +8812973 +8812937 +8811789 +8811865 +8811803 +8812859 +8811681 +8811631 +8811881 +8811863 +8812985 +8812253 +8813013 +8812511 +8812405 +8812643 +8815835 +8815847 +8814039 +8814177 +8815909 +8814345 +8816327 +8816333 +8814603 +8814687 +8816031 +8814467 +8815923 +8816359 +8816301 +8815331 +8816305 +8816303 +8815159 +8815103 +8815093 +8815061 +8814803 +8814875 +8814925 +8814905 +8814801 +8814715 +8814697 +8814487 +8814101 +8815899 +8815917 +8815883 +8814185 +8814175 +8814187 +8815933 +8814453 +8814337 +8813995 +8812559 +8811811 +8811801 +8811787 +8811699 +8813005 +8813147 +8814261 +8814435 +8814527 +8816357 +8814681 +8814647 +8814607 +8815179 +8815255 +8815175 +8816227 +8815467 +8815453 +8815367 +8815343 +8815267 +8815341 +8815329 +8815415 +8815411 +8815469 +8815487 +8815493 +8815337 +10524965 +10524945 +10525207 +10525355 +10527857 +10527933 +10528363 +10527955 +10528361 +10524935 +10524931 +10525189 +10525187 +10525325 +10525151 +10525153 +10525135 +10525125 +10525107 +10525101 +10525099 +10525069 +10525287 +10525025 +10525023 +10524987 +10524971 +10524963 +10524959 +10524951 +10524949 +10524941 +10525279 +10524943 +10525185 +10525333 +10525197 +10525201 +10525343 +10527843 +10527839 +10527837 +10527833 +10525233 +10532827 +10532829 +10527899 +10527891 +10527889 +10527881 +10527877 +10527871 +10527867 +10527865 +10527999 +10527993 +10527989 +10528355 +10527987 +10528353 +10528347 +10528339 +10528335 +10527939 +10528407 +10527997 +10528003 +10528001 +10528005 +10528007 +10528365 +10527991 +10528337 +10527953 +10528367 +10527983 +10527959 +10527853 +10528021 +10525227 +10525083 +10528417 +10525059 +10525313 +10525315 +10528037 +10528045 +10528043 +10528041 +10527971 +10527965 +10527855 +10528369 +10525219 +10525223 +10525291 +10525095 +10525097 +10528017 +10525297 +10528047 +10524933 +10525327 +10525307 +10525027 +10525303 +10525057 +10525347 +10525021 +10524983 +10524957 +10524955 +10524953 +10524961 +10524921 +10524937 +10525277 +10524989 +10524967 +10525013 +10525063 +10525051 +10525067 +10525129 +10525133 +10525349 +10525301 +10525329 +10525191 +10525215 +10525323 +10525331 +10525335 +10525199 +10525205 +10525203 +10525213 +10525351 +10525231 +10525235 +10525357 +10527847 +10527861 +10527883 +10527875 +10527863 +10527859 +10527869 +10527873 +10527885 +10527893 +10527901 +10528409 +10527895 +10527927 +10528349 +10528351 +10528415 +10527977 +10527967 +10527961 +10527949 +10527963 +10527947 +10528345 +10527925 +10527919 +10527917 +10527931 +10527937 +10528411 +10528413 +10527957 +10527975 +10527969 +10527995 +10528357 +10527981 +10528373 +10528011 +10528371 +10528009 +10524927 +10525305 +10525339 +10525123 +10525337 +10525061 +10525295 +10524985 +10532825 +10524981 +10532823 +10524973 +10524969 +10524947 +10524929 +10525009 +10525011 +10525031 +10525085 +10525293 +10525193 +10525341 +10525209 +10525217 +10525211 +10525221 +10527841 +10527835 +10527831 +10525345 +10528393 +10525229 +10528391 +10527887 +10528395 +10527905 +10527907 +10527909 +10527913 +10528403 +10527903 +10528397 +10528399 +10527897 +10527911 +10527915 +10528401 +10527985 +10527979 +10527973 +10528341 +10527941 +10527929 +10527923 +10527921 +10528405 +10527951 +10527943 +10527935 +10527945 +10528343 +10528359 +10528013 +10528023 +10528035 +10528015 +10528031 +10528019 +10528029 +10524925 +10524923 +10527849 +10525281 +10528027 +10528025 +10525159 +10527845 +10525321 +10527467 +10524595 +10526549 +10524491 +10526565 +10526541 +10526537 +10526517 +10526483 +10526431 +10526375 +10526369 +10526359 +10526327 +10526317 +10526303 +10526295 +10526253 +10526245 +10526233 +10526165 +10526143 +10526149 +10527193 +10527181 +10527179 +10526091 +10526081 +10526069 +10526067 +10526053 +10526059 +10526047 +10527139 +10527147 +10526039 +10527097 +10525953 +10525935 +10527089 +10525909 +10527081 +10527059 +10527019 +10525757 +10525747 +10526999 +10525733 +10526981 +10526977 +10525689 +10525679 +10525677 +10525669 +10526921 +10526923 +10526907 +10525635 +10526903 +10524503 +10525623 +10524493 +10524477 +10524473 +10524465 +10524467 +10524461 +10524449 +10524445 +10524423 +10524413 +10524407 +10524395 +10524381 +10524373 +10524701 +10524265 +10524687 +10524203 +10524641 +10524007 +10524003 +10524005 +10524637 +10524631 +10523975 +10523969 +10523959 +10523949 +10523951 +10524611 +10524607 +10523927 +10523925 +10523889 +10524601 +10524603 +166755121 +166755122 +10523883 +10523863 +10524939 +10524415 +10524411 +10524727 +10524393 +10524377 +10524345 +10524317 +10524301 +10524295 +10524303 +10524305 +10524267 +10524269 +10524261 +10524257 +10524259 +10524249 +10524255 +10524241 +10524229 +10524231 +10524201 +10524199 +10524197 +10524159 +10524155 +10524157 +10524151 +10524139 +10524141 +10524125 +10524123 +10525195 +10524113 +10526939 +10526915 +10525639 +10525641 +10525643 +10525649 +10525647 +10526931 +10525651 +10527077 +10527073 +10525883 +10527029 +10525827 +10525857 +10525859 +10525843 +10525841 +10527039 +10527041 +10525831 +10527043 +10525829 +10525823 +10525809 +10526169 +10526187 +10526189 +10526179 +10526171 +10526183 +10526181 +10526185 +10526177 +10526175 +10526173 +10527437 +10526591 +10526525 +10526515 +10526477 +10526473 +10526469 +10524609 +10523935 +10523905 +10523887 +10523867 +10523877 +10526613 +10526583 +10526563 +10526553 +10526905 +10525613 +10527879 +10525997 +10526383 +10526063 +10526979 +10524695 +10524679 +10524293 +10524733 +10525763 +10525785 +10527335 +10526569 +10526571 +10526535 +10526527 +10527273 +10527269 +10526419 +10526357 +10527165 +10527151 +10527157 +10527031 +10525791 +10525777 +10525965 +10525973 +10526005 +10526021 +10526003 +10526011 +10525999 +10526385 +10526429 +10526427 +10526425 +10526929 +10527473 +10525659 +10524673 +10524179 +10524677 +10524195 +10524219 +10524215 +10524691 +10524307 +10524361 +10524399 +10524403 +10527175 +10524725 +10525759 +10527009 +10525743 +10525729 +10525723 +10527215 +10526259 +10523973 +10523943 +10523971 +10524625 +10524281 +10524315 +10524709 +10524711 +10525771 +10527475 +10526585 +10526593 +10526475 +10526479 +10526645 +10526439 +10527265 +10526421 +10526417 +10527283 +10525755 +10525745 +10526983 +10525687 +10525699 +10526951 +10526945 +10525645 +10524741 +10524627 +10523909 +10523907 +10523903 +10523865 +10523879 +10523859 +10524591 +10523891 +10524605 +10523855 +10523853 +10523871 +10523893 +10523847 +10523845 +10524593 +10523919 +10523897 +10523901 +10523965 +10523923 +10523941 +10524645 +10524055 +10524647 +10524653 +10524663 +10524717 +10524697 +10524227 +10524667 +10524057 +10524095 +10524097 +10524049 +10524061 +10524671 +10524669 +10524661 +10524659 +10524083 +10523979 +10523999 +10524617 +10524615 +10524613 +10524309 +10525353 +10524469 +10524471 +10524721 +10524715 +10524327 +10524735 +10524497 +10524495 +10524489 +10527455 +10524487 +10526917 +10526927 +10525697 +10525709 +10525667 +10525683 +10526919 +10526933 +10526953 +10525719 +10525727 +10525705 +10525703 +10525715 +10525761 +10526989 +10526985 +10527005 +10527037 +10527045 +10525789 +10525779 +10527033 +10525821 +10525807 +10527047 +10525875 +10527061 +10527459 +10525891 +10527049 +10525885 +10527075 +10527087 +10525949 +10527129 +10525945 +10525967 +10525975 +10525961 +10525963 +10525979 +10525981 +10525915 +10525993 +10525911 +10525941 +10525895 +10527099 +10526045 +10526055 +10526061 +10527143 +10527163 +10527169 +10526107 +10527189 +10527191 +10526167 +10526161 +10526087 +10527463 +10526079 +10526097 +10526077 +10526075 +10526965 +10526899 +10523933 +10523913 +10523915 +10523875 +10524747 +10523873 +10524745 +10524599 +10524597 +10523869 +10523861 +10523857 +10523851 +10523849 +10523885 +10523957 +10523955 +10523977 +10524019 +10524017 +10524011 +10524027 +10524643 +10524175 +10524185 +10524689 +10524313 +10524723 +10524375 +10524409 +10524699 +10524773 +10524225 +10524683 +10524105 +10524099 +10524079 +10524103 +10524771 +10524081 +10524053 +10524681 +10524657 +10524085 +10524015 +10523993 +10523983 +10524013 +10523991 +10524665 +10525225 +10524433 +10524359 +10524211 +10524193 +10524181 +10524171 +10524167 +10524191 +10524177 +10524169 +10524165 +10524217 +10524209 +10524233 +10524247 +10524237 +10524239 +10524235 +10524707 +10524437 +10524425 +10524781 +10524289 +10524297 +10524343 +10524341 +10524329 +10524337 +10524713 +10524355 +10524353 +10524357 +10524367 +10524369 +10524365 +10524331 +10524277 +10524285 +10524287 +10524263 +10524273 +10524321 +10524283 +10524311 +10524347 +10524379 +10524447 +10524431 +10524451 +10524463 +10524459 +10524453 +10524443 +10524439 +10524427 +10524441 +10524429 +10524455 +10524475 +10524479 +10526897 +10526901 +10526909 +10526913 +10526911 +10525621 +10527457 +10525615 +10525619 +10526935 +10525681 +10525701 +10526987 +10525721 +10525713 +10525839 +10527477 +10527063 +10525881 +10526007 +10525985 +10525987 +10526001 +10527101 +10527095 +10525943 +10525923 +10525921 +10527429 +10526035 +10527141 +10527149 +10526083 +10527171 +10526141 +10526133 +10526163 +10526111 +10527185 +10526135 +10527187 +10526099 +10526085 +10527211 +10526287 +10526311 +10527213 +10527217 +10526215 +10526227 +10526231 +10526247 +10526329 +10526255 +10527465 +10526331 +10526345 +10526341 +10526347 +10526399 +10526423 +10526381 +10526437 +10526489 +10526451 +10526519 +10526557 +10527339 +10526611 +10526545 +10526547 +10526543 +10526567 +10526619 +10526587 +10524589 +10523843 +10524621 +10524619 +10524291 +10527015 +10523921 +10524635 +10523939 +10524633 +10524623 +10524001 +10525769 +10526397 +10524675 +10524731 +10525359 +10526237 +10526257 +10526263 +10526265 +10526387 +10526435 +10526561 +10526629 +10526625 +10526621 +10526631 +10526607 +10526605 +10526609 +10523841 +10524323 +10524251 +10524205 +10524279 +10524299 +10524213 +10524319 +10524243 +10524333 +10524351 +10524325 +10524729 +10527013 +10532713 +10527131 +10526025 +10526415 +10527493 +10523931 +10524385 +10526471 +10532831 +10527145 +10524703 +10524271 +10524253 +10524685 +10526925 +10525995 +10526027 +10523917 +10524705 +10527285 +10523881 +10523961 +10524009 +10524383 +10524207 +10524163 +10524245 +10524335 +10524349 +10524339 +10524275 +10527491 +10527023 +26330555 +10524749 +10524751 +10524753 +10527431 +10527433 +10527435 +26330566 +166755118 +166755119 +166755120 +10976557 +10524093 +10526623 +10526589 +10526575 +10526509 +10527301 +10526505 +10526497 +10527317 +10527343 +10527347 +10526595 +10526603 +10526599 +10527345 +10526071 +10526073 +10527183 +10526127 +10526125 +10526129 +10526121 +10526109 +10527123 +10526009 +10525989 +10525971 +10525929 +10525889 +10525849 +10526103 +10526065 +10526051 +10527331 +10527053 +10527025 +10524183 +10524153 +10524129 +10524117 +10524107 +10524101 +10524091 +10524087 +10524075 +10524069 +10524065 +10524063 +10524649 +10524051 +10524041 +10524025 +10524021 +10524693 +10524189 +10524161 +10524143 +10524127 +10523987 +10524067 +10524719 +10526031 +10525753 +10525749 +10525797 +10527051 +10525795 +10525833 +10527057 +10527065 +10527071 +10525905 +10527067 +10527083 +10525851 +10525853 +10525907 +10526457 +10527311 +10527307 +10527309 +10527341 +10527469 +10526627 +10527323 +10526355 +10526499 +10526521 +10527275 +10526441 +10526389 +10526251 +10526305 +10526211 +10526229 +10526201 +10526205 +10526191 +10526219 +10527263 +10526373 +10526401 +10527219 +10526321 +10526221 +10526235 +10526193 +10526197 +10527207 +10526157 +10526159 +10527413 +10527287 +10526465 +10526445 +10527295 +10527127 +10527105 +10526049 +10527115 +10527111 +10527125 +10527133 +10527153 +10527119 +10525983 +10527103 +10527461 +10525947 +10525879 +10527055 +10525801 +10527021 +10527453 +10525767 +10526991 +10526975 +10524739 +10525741 +10525783 +10525735 +10526995 +10525685 +10525657 +10525631 +10525627 +10525695 +10525693 +10526967 +10524639 +10523997 +10523985 +10523963 +10523947 +10523953 +10524651 +10524059 +10524039 +10524037 +10524045 +10527177 +10526101 +10524371 +10524387 +10524391 +10524421 +10524419 +10524405 +10527135 +10525773 +10525751 +10525803 +10526503 +10527333 +10526577 +10527439 +10526573 +10526581 +10526601 +10526597 +10527289 +10527293 +10526487 +10526443 +10526393 +10526501 +10526463 +10527291 +10527319 +10527279 +10526453 +10527261 +10527257 +10527255 +10526307 +10526239 +10526225 +10526209 +10526203 +10526241 +10526217 +10526391 +10526353 +10526319 +10526291 +10526261 +10527221 +10526289 +10526243 +10526213 +10526207 +10526199 +10526195 +10526447 +10526507 +10526023 +10527107 +10527109 +10527117 +10526033 +10527121 +10526019 +10526015 +10526041 +10526043 +10526057 +10526013 +10525951 +10525937 +10527093 +10525811 +10526997 +10525731 +10525725 +10525707 +10525691 +10526955 +10525663 +10525765 +10526963 +10526961 +10526971 +10526969 +10526973 +10525711 +10526949 +10526937 +10524507 +10525629 +10524779 +10525739 +10525737 +10527001 +10527003 +10525671 +10526947 +10526943 +10526941 +10526957 +10525637 +10524505 +10525661 +10525675 +10525655 +10525653 +10525717 +10525625 +10524737 +10524401 +10524457 +10524481 +10524435 +10524397 +10524389 +10524363 +10524221 +10524223 +10524115 +10524187 +10524655 +10524089 +10524073 +10524035 +10524033 +10524029 +10524031 +10523981 +10523989 +10524783 +10523929 +10523995 +10524629 +10523911 +10525617 +10525633 +10526959 +10525673 +10525665 +10526993 +10524417 +10524775 +10524777 +10524145 +10524763 +10524759 +10524043 +10524047 +10523967 +10524757 +10523945 +10523937 +10524755 +10524023 +10524071 +10524077 +10524111 +10524121 +10524131 +10524137 +10524149 +10524147 +10524135 +10524109 +10524119 +10527351 +10526105 +10526095 +10526131 +10527201 +10527197 +10527205 +10526153 +10526155 +10526151 +10526037 +10526017 +10525847 +10525817 +10525917 +10525919 +10525931 +10525991 +10526029 +10527137 +10527155 +10526089 +10527159 +10525793 +10527007 +10525775 +10527017 +10525805 +10525893 +10525939 +10525955 +10527085 +10525877 +10527167 +10526093 +10527173 +10527203 +10526139 +10527199 +10526145 +10527113 +10525969 +10525933 +10526117 +10526119 +10526123 +10525819 +10525869 +10525867 +10525873 +10525871 +10525865 +10526529 +10526531 +10527315 +10526539 +10525781 +10527027 +10525835 +10525825 +10525813 +10525837 +10525855 +10527035 +10525845 +10525861 +10525863 +10525903 +10525901 +10527079 +10525913 +10527091 +10526449 +10526523 +10527303 +10526511 +10527011 +10525787 +10525799 +10525815 +10527069 +10526481 +10527161 +10524173 +10524133 +10524761 +10524765 +10524767 +10524769 +10527423 +10527425 +10527427 +10518842 +10518430 +10518738 +26324849 +10527361 +10525925 +10525899 +10526137 +10526459 +10526485 +10526579 +10527359 +10527357 +10526641 +10527337 +10527415 +10527259 +10526493 +10527281 +10527253 +10526223 +10526271 +10526249 +10526315 +10527271 +10527299 +10527329 +10526617 +10527355 +10526615 +10527481 +10526467 +10526455 +10526433 +10527277 +10526371 +10526363 +10527231 +10526273 +10527249 +10526147 +10525927 +10525897 +10525887 +10526495 +10527321 +10527313 +10526513 +10527327 +10527325 +26324851 +10527479 +10526551 +10526555 +10526559 +10526461 +10527305 +10526491 +10527297 +10526533 +8893306 +8894464 +8893940 +8842291 +8842257 +8842183 +8842243 +8842185 +8842169 +8842201 +8894466 +8893296 +8894004 +8894000 +8893996 +8894252 +8894254 +8893994 +8893992 +8894230 +8894228 +8893956 +8894226 +8893850 +8893842 +8894192 +8894310 +8893810 +8894308 +8893790 +8893782 +8893774 +8893768 +8893760 +8893714 +8893700 +8893600 +8893568 +8893544 +8893476 +8893424 +8893420 +8893378 +8893372 +8893358 +8893320 +8893310 +8893350 +8893894 +8893848 +8894026 +8893886 +8893160 +8894160 +8894202 +8894190 +8894314 +8894312 +8893832 +8893830 +8893838 +8893836 +8894028 +8893860 +8893862 +8893866 +8894198 +8893874 +8893878 +8893882 +8894150 +8893346 +8893336 +8893292 +8893204 +8893402 +8894152 +8893334 +8894422 +8894424 +8893154 +8893130 +8893224 +8893196 +8888396 +8888402 +8893116 +8893108 +8888394 +8893344 +8893298 +8893300 +8893308 +8893206 +8893168 +8893166 +8893338 +8893290 +8893288 +8893132 +8893124 +8893126 +8893114 +8893106 +8893128 +8894446 +8894454 +8893888 +8893890 +8893880 +8894452 +8893870 +8893858 +8894438 +8894442 +8894432 +8894440 +8893626 +8894436 +8893512 +8893356 +8893322 +8893294 +8893302 +8893330 +8894426 +8893362 +8893528 +8893778 +8893856 +8893868 +8893852 +8893834 +8893820 +8893798 +8894444 +8893806 +8893898 +8893904 +8894476 +8894240 +8894470 +8893930 +8893938 +8894462 +8893902 +8894458 +8893912 +8893926 +8894264 +8894262 +8894016 +8894008 +8894020 +8894018 +8893974 +8893986 +8894472 +8894460 +8894468 +8894478 +8894232 +8894474 +8893970 +8894482 +8894250 +8894484 +8894032 +8893104 +8893800 +8894200 +8893876 +8894196 +8894194 +8894180 +8893670 +8893672 +8894174 +8893608 +8893574 +8893522 +8893690 +8893688 +8893674 +8893676 +8893304 +8894284 +8894416 +8893312 +8893374 +8893348 +8893328 +8893352 +8893376 +8893560 +8893580 +8893598 +8893606 +8893604 +8893582 +8893462 +8893478 +8893548 +8893570 +8893602 +8893712 +8894176 +8893710 +8893706 +8893756 +8893762 +8893758 +8894178 +8894294 +8893630 +8894182 +8893840 +8893846 +8893854 +8893826 +8893816 +8893814 +8893812 +8893828 +8893844 +8894204 +8894222 +8893954 +8893952 +8893944 +8894236 +8894224 +8894216 +8893928 +8893924 +8893922 +8893914 +8893910 +8893920 +8893918 +8893932 +8893908 +8893900 +8894220 +8894370 +8894014 +8894002 +8894010 +8893978 +8893976 +8894384 +8893988 +8893964 +8893942 +8893958 +8893934 +8894418 +8894366 +8893990 +8894244 +8893984 +8893972 +8893968 +8893960 +8894234 +8893980 +8893966 +8893962 +8893982 +8894246 +8893998 +8894248 +8893640 +8893646 +8893642 +8893550 +8893566 +8893740 +8893734 +8893718 +8893470 +8893636 +8893634 +8893472 +8893644 +8893732 +8893754 +8893738 +8894172 +8893632 +8893728 +8893552 +8894296 +8894300 +8894298 +8894290 +8894288 +8894286 +8842195 +8842267 +8842253 +8842251 +8842245 +8842239 +8842221 +8842207 +8842203 +8842171 +8842187 +8842205 +8842199 +8842191 +8842275 +8842213 +8842283 +8842273 +8842719 +8842277 +8842225 +8842263 +8842167 +8842179 +8842177 +8842217 +8842235 +8842255 +8842269 +8842303 +8842289 +8842279 +8842721 +8842261 +8842215 +8842281 +8842197 +8842181 +8842193 +8842219 +8842227 +8842229 +8842233 +8842231 +8842223 +8842241 +8842247 +8842249 +8842237 +8842717 +8842189 +8842209 +8842211 +8842723 +8842725 +8842727 +8842729 +8842731 +8842733 +8842735 +8842737 +8842739 +8894012 +8894006 +8893864 +8894490 +8894494 +8894334 +8894492 +8893748 +8893746 +8893744 +8893742 +8893730 +8893724 +8893720 +8893692 +8893610 +8893500 +8893436 +8893438 +8893428 +8893398 +8893396 +8893368 +8894158 +8893360 +8893342 +8893324 +8893276 +8893268 +8893262 +8893250 +8893246 +8893218 +8893198 +8893172 +8893174 +8893182 +8893184 +8895516 +8893180 +8893152 +8895510 +8895490 +8895440 +8895420 +8895362 +8896564 +8896526 +8896522 +8896404 +8897336 +8896240 +8896344 +8894186 +8897344 +8896696 +8897694 +8897696 +8897660 +8896518 +8896534 +8893884 +8894270 +8894154 +8896646 +8898258 +8894450 +8896176 +8897340 +8897342 +8893818 +8893824 +8894188 +8894434 +8895956 +8896080 +8894430 +8893316 +8893214 +8893146 +8888398 +8893110 +8893112 +8888400 +8888408 +8891126 +8895312 +8898418 +8897360 +8896770 +8897346 +8897708 +8897348 +8898336 +8897356 +8896516 +8896536 +8897644 +8897646 +8893796 +8893802 +8896524 +8903078 +8896238 +8896252 +8896428 +8896566 +8896548 +8893822 +8896550 +8898402 +8893652 +8893558 +8893538 +8893468 +8893456 +8893444 +8893430 +8893416 +8893400 +8893390 +8893386 +8893380 +8894164 +8897328 +8894162 +8896050 +8896016 +8894168 +8896046 +8896014 +8895766 +8896054 +8894166 +8893404 +8893392 +8893382 +8893388 +8893406 +8893418 +8893496 +8893494 +8893448 +8893486 +8893474 +8893576 +8893648 +8893654 +8894170 +8893332 +8894148 +8894292 +8894146 +8893248 +8893244 +8893242 +8893162 +8893156 +8893148 +8893144 +8893142 +8893140 +8888404 +8893134 +8893150 +8893158 +8893164 +8894420 +8893216 +8893136 +8893122 +8888406 +8893120 +8893138 +8893118 +8895394 +8891180 +8895570 +8895518 +8895602 +8895610 +8898156 +8894428 +8893314 +8893354 +8893366 +8894448 +8894456 +8894210 +8894206 +8893896 +8893906 +8894218 +8893916 +8894214 +8894238 +8894256 +8894480 +8894486 +8894268 +8894258 +8894488 +8894274 +8894272 +8894276 +8894034 +8894038 +8897272 +8897274 +8897260 +8897254 +8894036 +8897220 +8895502 +8895512 +8893186 +8895708 +8893238 +8895658 +8893190 +8895628 +8895690 +8897436 +8897432 +8893212 +8893170 +8893178 +8893200 +8893194 +8893202 +8893188 +8893176 +8893192 +8893208 +8893240 +8893258 +8893252 +8893264 +8893270 +8893278 +8894144 +8893318 +8894156 +8893364 +8893370 +8893442 +8893526 +8893546 +8893542 +8893572 +8893586 +8893588 +8893584 +8893620 +8893698 +8893510 +8893482 +8893480 +8893490 +8893556 +8893656 +8893684 +8893618 +8893564 +8893590 +8893624 +8893596 +8893540 +8893532 +8893524 +8893440 +8893434 +8893446 +8893458 +8893504 +8893612 +8893686 +8893726 +8893736 +8893752 +8893750 +8893788 +8893784 +8893804 +8893808 +8893794 +8894320 +8894306 +8894316 +8894184 +8893792 +8893872 +8893892 +8894208 +8893936 +8894372 +8894212 +8893946 +8903138 +8894374 +8893948 +8893950 +8894496 +8894242 +8894260 +8894266 +8894030 +8897206 +8897202 +8897420 +8897218 +8897284 +8897256 +8897242 +8897244 +8897246 +8897276 +8897230 +8897238 +8897240 +8893394 +8893594 +8893256 +8893254 +8893696 +8893694 +8893678 +8893668 +8893666 +8893614 +8893554 +8893506 +8893466 +8893464 +8893460 +8893454 +8893502 +8893520 +8893518 +8893664 +8893616 +8893230 +8893282 +8893412 +8893410 +8893660 +8893704 +8893764 +8893716 +8893708 +8893772 +8893426 +8893232 +8893226 +8893220 +8893638 +8893622 +8893592 +8893658 +8893628 +8893562 +8893650 +8893766 +8893662 +8893682 +8893384 +8893286 +8893414 +8893408 +8893274 +8893260 +8893228 +8893272 +8893266 +8893450 +8893516 +8893514 +8893508 +8893530 +8893534 +8893484 +8893498 +8893488 +8893222 +8893210 +8893280 +8893284 +8893452 +8893536 +8893702 +8893680 +8893770 +8893776 +8893432 +8893786 +8893722 +8894142 +8893236 +8893780 +8893422 +8893578 +8893234 +8893492 +8894302 +8894304 +8894318 +8894322 +8894330 +8894336 +8894340 +8894344 +8894348 +8894368 +8894364 +8894360 +8894358 +8894356 +8894354 +8894352 +8894350 +8894410 +8894408 +8894406 +8894404 +8894402 +8894400 +8894398 +8894396 +8894394 +8894392 +8894390 +8894388 +8894386 +8894382 +8894380 +8894378 +8894376 +8897828 +8897826 +8897824 +8897822 +8897818 +8897816 +8897814 +8897812 +8897792 +8897954 +8897952 +8897944 +8897942 +8897930 +8897922 +8897920 +8897918 +8897916 +8898048 +8898046 +8898030 +8898028 +8898026 +8898024 +8898080 +8898078 +8903136 +8898404 +8898088 +8898062 +8898058 +8898056 +8898054 +8898052 +8898050 +8898004 +8897974 +8897966 +8897956 +8897898 +8897862 +8897860 +8897858 +8897842 +8897840 +8897832 +8897830 +8897790 +8897788 +8897786 +8897784 +8897782 +8897752 +8897748 +8897704 +8897692 +8897678 +8897676 +8897658 +8897656 +8897654 +8894414 +8894412 +8894346 +8894342 +8894338 +8894332 +8894328 +8894326 +8894324 +8897584 +8897604 +8897596 +8897586 +8897594 +8897446 +8897468 +8897448 +166755056 +166755057 +8842271 +8842259 +8842265 +166755082 +166755083 +8891152 +8891034 +26325148 +26325150 +8898430 +8898436 +8898038 +8895326 +8898380 +8895364 +8895314 +8891188 +8898382 +8895310 +8891190 +8891174 +8895322 +8891202 +8891206 +8895324 +8891178 +8896308 +8896260 +8896272 +8896190 +8896094 +8895908 +8895792 +8895664 +8895546 +8898158 +8898032 +8898022 +8898034 +8898432 +8898016 +8898036 +8897998 +8897986 +8897990 +8897962 +8897958 +8897946 +8897940 +8897880 +8897884 +8898302 +8896612 +8896684 +8897216 +8896604 +8898376 +8897424 +8897208 +8897228 +8897236 +8897194 +8896942 +8898414 +8896570 +8896624 +8896656 +8896658 +8896462 +8896574 +8896584 +8897684 +8896962 +8897932 +8897936 +8896900 +8896882 +8896828 +8896796 +8897164 +8897408 +8897146 +8897152 +8897154 +8897158 +8897160 +8897156 +8897144 +8897126 +8897136 +8897212 +8897416 +8897224 +8897222 +8896898 +8897882 +8896820 +8896754 +8896730 +8897250 +8897252 +8897258 +8898136 +8898134 +8898138 +26325146 +8897248 +8897418 +8897414 +8898368 +8898360 +8898362 +8897068 +8897090 +8897124 +8897120 +8897056 +8898372 +8897180 +8897200 +8897196 +8897186 +8897190 +8897130 +8897054 +8897038 +8897034 +8897382 +8897388 +8897390 +8897384 +8897412 +8897142 +8897112 +8897114 +8897148 +8897102 +8897104 +8897162 +8897182 +8897176 +8897210 +8897184 +8897178 +8897172 +8897410 +8897404 +8898428 +8897988 +8897020 +8897964 +8897378 +8897380 +8897960 +8897008 +8897018 +8897000 +8897010 +8896908 +8897892 +8897888 +8895504 +8895354 +8895330 +8898186 +8895768 +8896648 +8896670 +8898270 +8896390 +8898342 +8896752 +8898310 +8896602 +8896790 +8896870 +8896916 +8896922 +8898352 +8896772 +8896830 +8896872 +8896994 +8897046 +8897372 +8897094 +8897406 +8897080 +8897036 +8896984 +8897026 +8896918 +8896938 +8896874 +8896894 +8896732 +8896806 +8896686 +8896510 +8896572 +8896664 +8896230 +8896192 +8896048 +8898296 +8898290 +8896482 +8898256 +8896382 +8896430 +8895952 +8896132 +8896232 +8896078 +8896236 +8896354 +8896324 +8896328 +8898226 +8896044 +8898174 +8895662 +8898160 +8895770 +8895820 +8896586 +8896506 +8895932 +8895884 +8895730 +8895638 +8895654 +8895758 +8895660 +8895620 +8891166 +8895328 +8895442 +8895520 +8895506 +8895526 +8898152 +8895596 +8898154 +8896668 +8896540 +8896458 +8896740 +8896802 +8896688 +8896728 +8896822 +8897844 +8896910 +8898410 +8896932 +8897894 +8897924 +8897914 +8896914 +8896920 +8896912 +8896798 +8896884 +8897376 +8897970 +8897060 +8897996 +8897032 +8897110 +8898012 +8898434 +8897402 +8898020 +8897400 +8897398 +8897386 +8897028 +8897368 +8897366 +8896956 +8897358 +8896836 +8897354 +8896808 +8896234 +8896262 +8896600 +8896538 +8896578 +8896484 +8896494 +8896486 +8896246 +8896254 +8896336 +8896248 +8896142 +8896104 +8896090 +8895988 +8897504 +8896100 +8896152 +8896102 +8896160 +8896322 +8896340 +8896352 +8896338 +8896326 +8896086 +8895906 +8895548 +8895550 +8895824 +8895816 +8897490 +8895818 +8896334 +8896542 +8896440 +8896032 +8895946 +8895888 +8895890 +8895812 +8895782 +8897452 +8897450 +8895940 +8895788 +8895748 +8895778 +8897472 +8895704 +8895784 +8897462 +8895786 +8895746 +8895728 +8891146 +8891210 +8895396 +8895402 +8895318 +8897428 +8895460 +8895430 +8895464 +8895474 +8895564 +8895476 +8895494 +8895484 +8895540 +8891104 +8891054 +8898356 +8897278 +8898374 +8891106 +8898426 +8897900 +8897906 +8897904 +8898014 +8898006 +8898000 +8898002 +8898010 +8898420 +8898408 +8897896 +8898416 +8898406 +8897902 +8897910 +8897908 +8897984 +8897982 +8897976 +8897972 +8898424 +8897912 +8897938 +8897926 +8897934 +8897928 +8897886 +8897890 +8898412 +8897374 +8897016 +8898422 +8898044 +8898042 +8898040 +8898086 +8898082 +8898076 +8898074 +8898070 +8898068 +8898140 +8898132 +8898130 +8898128 +8898126 +8898124 +8898122 +8898120 +8898118 +8898116 +8898114 +8898112 +8898110 +8898108 +8898106 +8898104 +8898102 +8898100 +8898098 +8898096 +8898094 +8898092 +8898090 +8898066 +8898064 +8898018 +8898008 +8897980 +8897978 +8897622 +8897620 +8897618 +8897572 +8897566 +8897564 +8897562 +8897560 +8897554 +8897968 +8897950 +8897948 +8872372 +8872388 +8872394 +8872396 +8872398 +8872370 +8872390 +8872410 +8872960 +8872432 +8872422 +8872094 +8872088 +8872430 +8870458 +8870810 +8872412 +8870756 +8870676 +8870504 +8872400 +8872402 +8872962 +8870878 +8870674 +8870516 +8870762 +8870688 +8870686 +8870706 +8870880 +8870900 +8870918 +8870874 +8872424 +8870514 +8870766 +8872364 +8870780 +8872362 +8872360 +8872368 +8870794 +8872378 +8872952 +8870876 +8872374 +8872384 +8872386 +8872392 +8872382 +8872404 +8872380 +8870806 +8872376 +8872950 +8892134 +8889634 +8897878 +8897876 +8897872 +8897838 +8897798 +8897778 +8897802 +8897796 +8897808 +8897804 +8897806 +8897768 +8897776 +8897754 +8897750 +8897766 +8897758 +8897764 +8897716 +8897742 +8897724 +8897700 +8897670 +8897662 +8897634 +8898400 +8896174 +8897610 +8896030 +8895984 +8897322 +8897318 +8895864 +8895796 +8895712 +8895670 +8895636 +8895578 +8895542 +8895436 +8895390 +8895376 +8891212 +8891068 +8897608 +8897578 +8897606 +8897592 +8898394 +8897582 +8897612 +8897568 +8897552 +8897538 +8897528 +8891048 +8891042 +8891000 +8890962 +8890782 +8891494 +8890434 +8890366 +933030003 +933030004 +8889880 +8889864 +8889774 +8889750 +8889692 +8896980 +8895780 +8896736 +8891198 +8895468 +8891920 +8897166 +8896310 +8891510 +8890946 +8896880 +8896418 +8897664 +8896424 +8896426 +8896394 +8897350 +8897772 +8896738 +8896756 +8896766 +8896768 +8896786 +8896792 +8896834 +8896950 +8896948 +8896958 +8896982 +8896620 +8897738 +8897740 +8896502 +8896454 +8896266 +8896226 +8896060 +8896022 +8895898 +8896856 +8897866 +8896842 +8896850 +8896974 +8896122 +8897570 +8896098 +8891234 +8891208 +8895832 +8895892 +8897522 +8897524 +8895682 +8895608 +8890240 +8890038 +8889878 +8889852 +8889786 +8897122 +8898072 +8897138 +8897128 +8897132 +8897150 +8896742 +8897794 +8896720 +8896642 +8896636 +8896630 +8896478 +8895606 +8895672 +8897470 +8895566 +8895498 +8895350 +8895368 +8895342 +8895438 +8895392 +8897434 +8897430 +8891214 +8895360 +8898384 +8891140 +8891022 +8897362 +8897066 +8897058 +8897092 +8897048 +8897044 +8898364 +8897394 +8897078 +8898366 +8897192 +8897204 +8898370 +8897214 +8898354 +8896924 +8896978 +8897012 +8897392 +8897022 +8896852 +8897006 +8896886 +8896888 +8896774 +8896780 +8896750 +8898344 +8896722 +8896734 +8896788 +8896876 +8896810 +8897352 +8896588 +8896590 +8896562 +8896300 +8898274 +8896392 +8896508 +8898250 +8896214 +8897118 +8897064 +8897116 +8897088 +8897098 +8898060 +8897134 +8897108 +8897076 +8897106 +8897086 +8897050 +8897084 +8897052 +8897140 +8897170 +8897168 +8897188 +8897198 +8897174 +8896946 +8896928 +8896954 +8896960 +8896972 +8897014 +8896992 +8896966 +8897992 +8896840 +8896868 +8896760 +8897800 +8897820 +8897810 +8896748 +8897780 +8896764 +8896716 +8896712 +8897718 +8897714 +8896528 +8897702 +8896546 +8896406 +8896302 +8896278 +8896320 +8897640 +8898398 +8897530 +8892174 +8890674 +8890620 +8892116 +8890648 +8890036 +8889850 +8891940 +8890524 +8890586 +8892098 +8890556 +8890374 +8890334 +8890546 +8889738 +8892032 +8892034 +8892030 +8892002 +8890060 +8891932 +8891934 +8891444 +8889682 +8889678 +8891930 +8889776 +8891962 +8891120 +8891130 +8890594 +8890622 +8890572 +8889788 +8889772 +8889780 +8889884 +8889898 +8889870 +8889872 +933030007 +933030008 +933030005 +933030006 +8890396 +8890368 +8890346 +8890448 +8891622 +8890506 +8890450 +8891638 +8890512 +8890518 +8891470 +8892044 +8890320 +933030001 +933030002 +8891452 +8890116 +8890094 +8890058 +8889794 +8890166 +8890164 +8891464 +8890158 +8890140 +8890064 +8889740 +8889796 +8891440 +8889752 +8889882 +8890990 +8891500 +8892164 +8890890 +8892172 +8892132 +8892124 +8892198 +8890818 +8890816 +8890940 +8892294 +8892290 +8895616 +8898170 +8898176 +8895840 +8895828 +8898190 +8896056 +8896168 +8897324 +8896010 +8898192 +8895874 +8898180 +8895760 +8898168 +8898164 +8895452 +8895332 +8891060 +8895372 +8895358 +8891110 +8891012 +8895580 +8898162 +8895366 +8895472 +8895334 +8895336 +8897308 +8895772 +8898172 +8895618 +8895958 +8896028 +8897330 +8898262 +8898268 +8896368 +8896452 +8896224 +8896156 +8895900 +8895978 +8896194 +8896652 +8898332 +8896678 +8898338 +8898248 +8896594 +8898312 +8896644 +8891506 +8890784 +8890780 +8890672 +8890670 +8891038 +8890938 +8890854 +8890852 +8891216 +8891196 +8895684 +8895646 +8895648 +8895918 +8897536 +8895998 +8897550 +8897574 +8897576 +8896106 +8897590 +8897580 +8898396 +8895904 +8895950 +8895902 +8895756 +8895594 +8895590 +8895554 +8895630 +8895556 +8895374 +8895370 +8895346 +8891070 +8891138 +8891020 +8895500 +8895478 +8897438 +8897440 +8898386 +8891218 +8892260 +8897304 +8895406 +8897312 +8895688 +8895642 +8897630 +8897632 +8896422 +8897668 +8897334 +8896376 +8896500 +8896258 +8896058 +8896066 +8895996 +8895976 +8895954 +8896024 +8896268 +8897756 +8896680 +8896706 +8897770 +8897774 +8896676 +8896718 +8896710 +8896708 +8896628 +8896476 +8896450 +8896638 +8896596 +8896606 +8896804 +8897834 +8897836 +8896812 +8897852 +8897848 +8896864 +8897870 +8897874 +8897868 +8897854 +8897850 +8896862 +8897864 +8897856 +8897364 +8891600 +8891722 +8891726 +8891728 +8872092 +8872090 +8870842 +8870792 +8870614 +8870752 +8872408 +8870610 +8872074 +8870534 +8870466 +8870448 +8870412 +8870364 +8872084 +8870860 +8871974 +8872066 +8870274 +8870186 +8870138 +8870022 +8872046 +8869908 +8869920 +8870446 +8870360 +8870244 +8870196 +8870830 +8870862 +8870574 +8872732 +8870484 +8870500 +8870308 +8870298 +8870188 +8870020 +8869898 +8871960 +8869940 +8870010 +8872682 +8870124 +8872688 +8870112 +8871976 +8869938 +8870742 +8870772 +8870592 +8870498 +8870536 +8870306 +8872036 +8869904 +8871972 +8872034 +8872056 +8870142 +8870358 +8870242 +8870194 +8870106 +8870056 +8870040 +8869962 +8869964 +8869996 +8870572 +8870546 +8870602 +8870872 +8869812 +8869810 +8870518 +8870474 +8870544 +8870696 +8870664 +8870680 +8870828 +8889566 +8897506 +8897500 +8897488 +8897492 +8897476 +8897482 +8897464 +8891242 +8891240 +8895320 +8897442 +8895378 +8891232 +8891192 +8891108 +8891876 +8891874 +8897444 +8891866 +8891860 +8892374 +8891850 +8892370 +8891842 +8890878 +8892366 +8890864 +8890696 +8890598 +8891830 +8890846 +8892362 +8891488 +8890588 +8891738 +8891732 +8891714 +8891704 +8891678 +8890356 +8891640 +8890128 +8890074 +8890034 +8889868 +8889760 +8889554 +8890032 +8889936 +8889586 +8889564 +8891384 +8889526 +8890236 +8892356 +8891642 +8896902 +8889466 +8890234 +8889920 +8897396 +8896012 +8889644 +8896674 +8889464 +8896038 +8895344 +8891084 +8891852 +8891090 +8891080 +8891040 +8891018 +8890874 +8890832 +8890820 +8890770 +8891078 +8891098 +8891096 +8891032 +8891118 +8891132 +8891164 +8896460 +8896466 +8896468 +8896442 +8896402 +8896374 +8896362 +8896318 +8896296 +8896330 +8896332 +8896370 +8896436 +8896438 +8896832 +8896844 +8896892 +8889524 +8889488 +8889472 +8890520 +8891706 +8891712 +8890504 +8890418 +8890360 +8890322 +8890168 +8890088 +8890042 +8896986 +8897024 +8896026 +8895992 +8895962 +8895562 +8897498 +8889766 +8889762 +8889734 +8889716 +8889686 +8889674 +8896582 +8895480 +8891254 +8895528 +8890468 +8891682 +8890452 +8890380 +8889580 +8889578 +8891378 +8889514 +8896976 +8898358 +8897002 +8896952 +8896800 +8898350 +8896854 +8896838 +8896906 +8896890 +8898272 +8896276 +8896446 +8898282 +8896360 +8896280 +8896018 +8898230 +8896202 +8896222 +8896398 +8896040 +8896622 +8896498 +8896560 +8896662 +8896632 +8896420 +8896456 +8898298 +8896640 +8898334 +8896492 +8896666 +8898328 +8896794 +8896692 +8896726 +8896746 +8898348 +8896614 +8896592 +8895916 +8896112 +8896188 +8896164 +8895922 +8898194 +8895866 +8895926 +8895666 +8898178 +8898184 +8895894 +8898200 +8895860 +8895754 +8898166 +8895426 +8892300 +8891082 +8892266 +8892178 +8890772 +8892236 +8892244 +8891030 +8892256 +8896970 +8896968 +8896988 +8896990 +8897994 +8896776 +8896778 +8896762 +8896846 +8896878 +8896904 +8896380 +8896274 +8896384 +8896158 +8896294 +8896020 +8896298 +8898294 +8896288 +8896286 +8896488 +8896448 +8896530 +8896470 +8896576 +8896616 +8896410 +8896412 +8896496 +8896514 +8896580 +8896520 +8896556 +8896558 +8896554 +8896552 +8896544 +8896608 +8896568 +8896682 +8896700 +8896714 +8896702 +8897846 +8896690 +8896704 +8896694 +8895936 +8896162 +8895914 +8895872 +8895870 +8898392 +8895868 +8897520 +8895798 +8897508 +8895800 +8895794 +8895574 +8897494 +8897496 +8895598 +8895514 +8895696 +8895692 +8897484 +8897502 +8895470 +8897456 +8897466 +8895416 +8897454 +8895352 +8897460 +8895532 +8897458 +8895572 +8895640 +8895686 +8895742 +8895674 +8898188 +8895356 +8895348 +8891172 +8891862 +8891002 +8890858 +8891832 +8890802 +8890822 +8890836 +8890922 +8890972 +8891846 +8895656 +8895534 +8895454 +8892306 +8892298 +8890754 +8890840 +8892142 +8890496 +8890610 +8891250 +8891200 +8891154 +8890848 +8892364 +8890838 +8891800 +8891796 +8890564 +8891708 +8890522 +8890172 +8889876 +8889844 +8889822 +8891960 +8889792 +8889784 +8889680 +8891916 +8891914 +8891376 +8891912 +8889470 +8889468 +8891902 +8891900 +8889584 +8891942 +8891946 +8891936 +8889516 +8891380 +8891382 +8891978 +8892000 +8892014 +8892046 +8892042 +8889988 +8889990 +8890514 +8892038 +8892062 +8892074 +8890238 +8892128 +8890650 +8890666 +8890752 +8890910 +8890694 +8892102 +933030009 +8890066 +8892026 +8892020 +8891954 +8891974 +8891966 +8889854 +8892254 +8892272 +8892280 +8891236 +8890186 +8892358 +8889754 +8889756 +8889720 +8889632 +8889560 +8889552 +8889544 +8889486 +8889656 +8889666 +8889642 +8889938 +8889902 +8889824 +8889748 +8889828 +8889826 +8891570 +8889908 +8890072 +8889940 +8890098 +8890136 +8890112 +8890010 +8890232 +8890324 +8890362 +8890422 +8891608 +8891618 +8890554 +8891730 +8890876 +8890872 +8890758 +8890596 +8890550 +933030010 +8890258 +8890084 +8890082 +8890068 +8890956 +8892368 +8891046 +8892372 +8891088 +8892376 +8891182 +8891864 +8891872 +8891094 +8891184 +8895522 +8897474 +8897486 +8895850 +8897510 +8897514 +8896818 +8896860 +8896784 +8896814 +8896858 +8897526 +8897518 +8898390 +8897516 +8897512 +8896782 +8891662 +8891718 +8892386 +8891720 +933030011 +933030012 +8891664 +8891594 +8891624 +8891596 +8891544 +26325156 +26325154 +26325152 +8891574 +8891566 +8891564 +8891560 +8891558 +8891548 +8891546 +8891648 +8891602 +166755058 +8870376 +8872352 +8872350 +8870356 +8870334 +8872340 +8872354 +8872338 +8870342 +8870344 +8870278 +8870266 +8870232 +8870158 +8870096 +8870084 +8870052 +8870044 +8870026 +8870008 +8869974 +8869926 +8871986 +8869798 +8870426 +8870468 +8872366 +8872070 +8870564 +8870618 +8870128 +8872712 +8870566 +8870114 +8870126 +8870074 +8870316 +8870318 +8870226 +8869298 +8869514 +8869500 +8869448 +8870042 +8869914 +8871980 +8869842 +8869818 +8869892 +8872672 +8869986 +8870190 +8870492 +8870336 +8870222 +8870192 +8870156 +8870000 +8872680 +8869958 +8869850 +8869942 +8869816 +8869748 +8869814 +8872666 +8870014 +8869822 +8869676 +8872662 +8872656 +8869610 +8869684 +8869778 +8869608 +8872622 +8869998 +8870100 +8870350 +8870428 +8872356 +8870276 +8870216 +8870094 +8870078 +8869970 +8870080 +8872300 +8872296 +8869922 +8869852 +8871978 +8871982 +8869824 +8869826 +8871984 +8869796 +8869792 +8869790 +8869752 +8869746 +8869728 +8869678 +8869672 +8869450 +8872714 +8872702 +8870122 +8870134 +8872696 +8872720 +8872736 +8870382 +8872744 +8870526 +8872750 +8870678 +8870854 +8870924 +8870746 +8870590 +8872754 +8872786 +8870722 +8872768 +8870616 +8870980 +8870754 +8870774 +8872790 +8872788 +8869592 +8869612 +8869954 +8870168 +8870062 +8870104 +8870144 +8870214 +8872708 +8870338 +8870380 +8870394 +8872730 +8870340 +8870410 +8870558 +8870464 +8870462 +8870438 +8872742 +8870440 +8870620 +8870488 +8870604 +8870922 +8870818 +8870802 +8870804 +8870666 +8870668 +8870656 +8872776 +8870840 +8870884 +8870770 +8870882 +8870768 +8870892 +8870812 +8870814 +8870898 +8870834 +8870832 +8869638 +8869640 +8869750 +8889456 +8891540 +8891542 +8889490 +8891536 +8891534 +8890252 +8890242 +8890228 +8890190 +8890104 +8891448 +8889858 +8889808 +8889764 +8889640 +8891392 +8889538 +8889534 +8891450 +8889922 +8889866 +8889896 +8889888 +8889950 +8889952 +8890026 +8890028 +8890558 +8890618 +8898346 +8896414 +8892170 +8896342 +8896170 +8890466 +8890338 +8891492 +8890608 +8890570 +8890562 +8890978 +8890994 +8890974 +8891496 +8890844 +8890716 +8891822 +8890686 +8891786 +8891498 +8890960 +8891504 +8890884 +8890862 +8890808 +8890908 +8890950 +8891238 +8891222 +8891248 +8891262 +8891516 +8898388 +8895408 +8895584 +8895644 +8895740 +8895876 +8892352 +8891260 +8895340 +8891258 +8895384 +8897306 +8895600 +8895626 +8895676 +8895744 +8895830 +8897628 +8895966 +8896000 +8896212 +8896290 +8897730 +8896316 +8897746 +8896504 +8897744 +8896400 +8896364 +8896372 +8896388 +8896444 +8897338 +8895726 +8890970 +8890834 +8890704 +8891486 +8895854 +8895964 +8895974 +8897648 +8896070 +8896082 +8896092 +8896204 +8895612 +8895622 +8895970 +8890204 +8891466 +8890294 +8890330 +8890326 +8890314 +8891686 +8896350 +8896304 +8896306 +8898306 +8896378 +8896408 +8898326 +8896464 +8896626 +8896598 +8898330 +8896650 +8890678 +8892188 +8892150 +8892136 +8892166 +8892230 +8890796 +8892210 +8891490 +8890792 +8892248 +8890582 +8890680 +8890662 +8892194 +8892222 +8890732 +8892278 +8890860 +8892246 +8892190 +8896366 +8896416 +8897732 +8896480 +8896386 +8897722 +8897720 +8896532 +8896618 +8896512 +8890646 +8890568 +8890560 +8890578 +8890830 +8890798 +8890702 +8890842 +8890828 +8891844 +8890794 +8890726 +8890718 +8890682 +8890664 +8890660 +8890584 +8890676 +8890932 +8890806 +8890748 +8890750 +8890728 +8891834 +8892242 +8891114 +8892292 +8891228 +8898182 +8891112 +8891076 +8892316 +8892318 +8892330 +8895434 +8898222 +8895968 +8895790 +8896292 +8896034 +8898300 +8896396 +8898340 +8896356 +8896348 +8896432 +8898284 +8898278 +8898246 +8898232 +8896052 +8898218 +8897326 +8897320 +8897310 +8895586 +8895462 +8895524 +8896004 +8898252 +8895750 +8892106 +8890176 +8890178 +8892060 +8891446 +8891972 +8891992 +8891956 +8889758 +8889630 +8891390 +8889530 +8889498 +8889510 +8889628 +8891976 +8891968 +8889874 +8890986 +8890988 +8891878 +8891880 +8890912 +8890982 +8891226 +8891266 +8895412 +8895492 +8895414 +8891220 +8891512 +8895410 +8891264 +8895398 +8895736 +8895624 +8898204 +8895774 +8895920 +8895776 +8896072 +8896110 +8896154 +8896244 +8897686 +8896074 +8896146 +8896256 +8896196 +8897698 +8896206 +8896182 +8896346 +8897736 +8897734 +8896472 +8896434 +8897760 +8897762 +8896250 +8895698 +8898212 +8896218 +8896180 +8896198 +8897690 +8896358 +8896136 +8896178 +8897682 +8897680 +8896068 +8895960 +8895878 +8895724 +8895804 +8895852 +8895982 +8895806 +8896036 +8895912 +8897616 +8897614 +8892378 +8891256 +8895338 +8897480 +8897478 +8895814 +8896062 +8890364 +8890230 +8890226 +8890102 +8890006 +8889856 +8889806 +8889638 +8889624 +8889536 +8889522 +8891396 +8889840 +8889712 +8889710 +8890160 +8890114 +8892054 +8889986 +8890062 +8892056 +8890124 +8892064 +8892070 +8890210 +8892100 +8892140 +8890532 +8890516 +8892146 +8892144 +8890424 +8891908 +8889492 +8889476 +8889512 +8889546 +8889568 +8891924 +8891404 +8891406 +8891938 +8891944 +8891948 +8891958 +8889714 +8889732 +8892010 +8890040 +8889962 +8890048 +8890050 +8891462 +8890090 +8890120 +8891604 +8890250 +8890162 +8890150 +8890148 +8890170 +8890304 +8890420 +8890382 +8890278 +8890332 +8891474 +8890498 +8889496 +8891538 +8889506 +8889502 +8889478 +8891352 +8891372 +8889454 +8891386 +8889742 +8889670 +8889660 +8889654 +8891426 +8889668 +8889602 +8889608 +8891432 +8889610 +8889614 +8889620 +8891554 +8891552 +8889676 +8891438 +8891562 +8889942 +8869262 +8870454 +8870848 +8872652 +8891770 +8891776 +8891768 +8869802 +8869788 +8869754 +8869704 +8869664 +8869636 +8869618 +8869554 +8871956 +8869572 +8869550 +8869528 +8869478 +8869440 +8869420 +8869362 +8872244 +8870682 +8870708 +8870906 +8870776 +8870480 +8870388 +8870372 +8870286 +8870268 +8870068 +8870018 +8869952 +8869912 +8869888 +8871996 +8869862 +8869848 +8869758 +8869744 +8869658 +8869630 +8870456 +8871990 +8872726 +8870224 +8872738 +8869956 +8870494 +8870270 +8872062 +8870086 +8870032 +8869990 +8870312 +8872358 +8870908 +8870820 +8870790 +8870764 +8870728 +8870646 +8870636 +8872064 +8870118 +8870166 +8870236 +8870346 +8869616 +8869230 +8869588 +8869506 +8872252 +8871952 +8869458 +8869416 +8869384 +8869326 +8869222 +8870894 +8870502 +8870672 +8870598 +8870414 +8870588 +8872778 +8872782 +8870600 +8870442 +8870280 +8872692 +8869988 +8869972 +8870102 +8869978 +8871992 +8869808 +8869760 +8869730 +8872670 +8869628 +8869644 +8869784 +8869896 +8869968 +8872700 +8870036 +8872706 +8870164 +8872734 +8870330 +8870460 +8870642 +8872766 +8870038 +8870116 +8870314 +8870162 +8872684 +8869540 +8869194 +8872602 +8869130 +8870902 +8870734 +8872076 +8872086 +8870670 +8870470 +8870634 +8870622 +8872446 +8870626 +8870650 +8870796 +8870478 +8870370 +8870092 +8870088 +8872320 +8872052 +8870182 +8870016 +8869950 +8871994 +8869860 +8869756 +8869742 +8869660 +8869846 +8869976 +8869984 +8870024 +8870090 +8870288 +8870206 +8870208 +8870228 +8870390 +8870362 +8870384 +8870778 +8870540 +8869634 +8869314 +8872630 +8869452 +8869534 +8872642 +8869364 +8869414 +8869418 +8872626 +8869552 +8869682 +8869674 +8872650 +8869732 +8869410 +8872612 +8869154 +8872596 +8872590 +8869508 +8869436 +8869438 +8869434 +8869594 +8869584 +8872264 +8872254 +8869476 +8869430 +8869412 +8872250 +8869360 +8869614 +8869626 +8869574 +8871958 +8869378 +8869354 +8872248 +8869218 +8869196 +8869198 +8869186 +8869190 +8869200 +8869182 +8869156 +8869228 +8872620 +8869486 +8869380 +8872618 +8872576 +8871940 +8872660 +8872004 +8871962 +8871966 +8869762 +8871988 +8869886 +8869948 +8872686 +8870130 +8872716 +8872718 +8870400 +8870562 +8872780 +8870816 +8872798 +8869136 +8872906 +8869116 +8869328 +8869484 +8872644 +8869510 +8869488 +8869428 +8869274 +8869426 +8869374 +8871934 +8869680 +8869706 +8872290 +8869770 +8869786 +8869854 +8870120 +8870082 +8870046 +8870048 +8870110 +8870238 +8870210 +8870212 +8870254 +8870476 +8870524 +8870716 +8869138 +8871928 +8872256 +8872258 +8872260 +8872262 +8872266 +8889518 +8889508 +8889494 +8891518 +8889462 +8891348 +8891370 +8891368 +8891358 +8891360 +8889966 +8890012 +8890008 +8889956 +8889994 +8889982 +8889944 +8889934 +8889918 +8889910 +8889974 +8889948 +8891606 +8890086 +8890108 +8890122 +8890142 +8890200 +8890202 +8890206 +8890308 +8890310 +8890342 +8891476 +8890376 +8891478 +8896284 +8898320 +8898244 +8889428 +8892120 +8890408 +8890580 +8890710 +8890756 +8890826 +8890824 +8890866 +8890920 +8890924 +8890942 +8890964 +8891050 +8891052 +8891006 +8891102 +8891156 +8891194 +8892354 +8891270 +8895382 +8895536 +8895558 +8895614 +8895732 +8895734 +8895762 +8895802 +8895838 +8897652 +8897650 +8895826 +8895702 +8895716 +8895822 +8895924 +8896120 +8896126 +8896200 +8895880 +8895928 +8896084 +8896096 +8896114 +8896116 +8896144 +8896216 +8896220 +8897706 +8896242 +8891204 +8895386 +8897600 +8895808 +8897602 +8895810 +8891356 +8890800 +8890760 +8890638 +8890510 +8890438 +8890248 +8891700 +8892168 +8892186 +8892232 +8892202 +8890574 +8892240 +8892264 +8890630 +8890604 +8890606 +8892258 +8890814 +8892296 +8892304 +8890930 +8892308 +8892314 +8898206 +8898202 +8895560 +8898210 +8891274 +8890944 +8890980 +8891158 +8891272 +8898208 +8898220 +8898242 +8898240 +8890636 +8890440 +8890444 +8890432 +8891758 +8891752 +8891760 +8891754 +8890530 +8890774 +8890746 +8890690 +8890744 +8890614 +8890576 +8891810 +8891806 +8890616 +8891808 +8890692 +8890804 +8890762 +8890764 +8890734 +8890628 +8890740 +8890724 +8890722 +8890602 +8892228 +8891824 +8890880 +8890868 +8891868 +8891870 +8891854 +8891858 +8890936 +8890906 +8890904 +8890966 +8891882 +8890928 +8890934 +8890954 +8895424 +8895432 +8897542 +8897544 +8895486 +8903130 +8895488 +8895538 +8895576 +8895592 +8895764 +8895714 +8897636 +8895668 +8895700 +8897626 +8898276 +8898292 +8898286 +8895896 +8895752 +8898314 +8898324 +8898316 +8896172 +8898288 +8898234 +8895482 +8895508 +8895604 +8891268 +8891092 +8895458 +8895842 +8898216 +8895846 +8895986 +8895910 +8896138 +8898318 +8898304 +8896270 +8896264 +8898266 +8896314 +8896312 +8898308 +8889426 +8891906 +8889434 +8891928 +8889556 +8889688 +8891984 +8891436 +8891400 +8891964 +8891398 +8889646 +8891918 +8891952 +8889746 +8891982 +8892008 +8892004 +8892048 +8889728 +8889862 +8892012 +8889904 +8892028 +8895834 +8895858 +8895848 +8895934 +8896006 +8896124 +8896088 +8896166 +8897726 +8897728 +8896008 +8897688 +8895938 +8895972 +8895948 +8895844 +8898260 +8895836 +8897642 +8897638 +8896128 +8896210 +8896140 +8895980 +8896064 +8896130 +8897710 +8895990 +8895738 +8895422 +8892380 +8895456 +8895588 +8891136 +8895552 +8898196 +8895496 +8895444 +8895718 +8897598 +8895882 +8897624 +8895942 +8895994 +8897674 +8895944 +8896118 +8896228 +8896134 +8896186 +8896184 +8895930 +8896282 +8896208 +8889440 +8889438 +8891346 +8891532 +8891528 +8891526 +8891530 +8889446 +8891524 +8891350 +8889500 +8891388 +8891402 +8891422 +8889604 +8889606 +8891428 +8891414 +8889588 +8889648 +8889520 +8889946 +8889744 +8889924 +8889906 +8889926 +8889886 +8889730 +8889690 +8891998 +8891576 +8889890 +8889860 +8889722 +8889812 +8891582 +8892108 +8892080 +8890126 +8892086 +8892076 +8890174 +8890194 +8892114 +8892126 +8890372 +8892130 +8892160 +8892180 +8890476 +8892284 +8890470 +8892138 +8892162 +8892118 +8892094 +8892122 +8892078 +8889968 +8890054 +8890118 +8891634 +8891636 +8890020 +8889996 +8891610 +8890044 +8891612 +8890002 +8892058 +8890110 +8890106 +8890198 +8890196 +8890188 +8890292 +8890388 +8890390 +8890480 +8892200 +8890352 +8892158 +8890612 +8890712 +8890776 +8890296 +8890398 +8890298 +8890400 +8890224 +8890134 +8890146 +8890262 +8890096 +8895380 +8891520 +8891278 +8897532 +8870912 +8870654 +8870624 +8870584 +8870576 +8870530 +8870432 +8870418 +8870368 +8870250 +8870220 +8870200 +8870148 +8880508 +8872896 +8869774 +8869720 +8869654 +8869578 +8869544 +8869536 +8869518 +8869494 +8869464 +8869394 +8869358 +8869252 +8869338 +8872242 +8869220 +8869174 +8869164 +8869152 +8869118 +8869068 +8868990 +8870006 +8869966 +8869932 +8869918 +8869916 +8869924 +8872044 +8872986 +8870942 +8870838 +8870824 +8870798 +8872484 +8872486 +8870748 +8870652 +8872016 +8869724 +8869472 +8869606 +8869334 +8868968 +8868950 +8872990 +8870374 +8870366 +8870292 +8870240 +8870264 +8870258 +8870416 +8870396 +8870284 +8870152 +8869698 +8870198 +8869882 +8869858 +8872006 +8872308 +8869766 +8870914 +8870782 +8870436 +8872762 +8870272 +8870218 +8872000 +8872678 +8869768 +8869710 +8869656 +8872658 +8869568 +8869602 +8869498 +8869482 +8869286 +8869462 +8869446 +8872638 +8869344 +8869232 +8869206 +8869054 +8869026 +8872988 +8869042 +8869044 +8872568 +8869034 +8869104 +8869214 +8872634 +8869234 +8869270 +8869290 +8869422 +8869424 +8869388 +8869348 +8869376 +8869604 +8869662 +8869712 +8869820 +8872012 +8872014 +8869236 +8869170 +8872606 +8869090 +8872584 +8872572 +8870910 +8870808 +8872968 +8872974 +8870528 +8870406 +8870282 +8870150 +8870098 +8870172 +8870320 +8870176 +8869890 +8869884 +8871998 +8869830 +8869836 +8872002 +8872304 +8872306 +8869718 +8869688 +8869620 +8869600 +8869576 +8869546 +8869652 +8869556 +8869548 +8872270 +8872272 +8869542 +8872648 +8869492 +8869460 +8869356 +8869352 +8869330 +8869346 +8869372 +8869370 +8869172 +8869160 +8869128 +8869132 +8869184 +8869192 +8869168 +8872234 +8872236 +8872232 +8869126 +8869110 +8869122 +8869162 +8869148 +8872230 +8869084 +8869074 +8869022 +8872992 +8869080 +8869046 +8869052 +8869016 +8869102 +8869150 +8869340 +8869392 +8869302 +8869294 +8869296 +8869306 +8869520 +8869538 +8869580 +8869490 +8869404 +8869402 +8869502 +8869772 +8869910 +8869146 +8872238 +8872240 +8869134 +8869076 +8869738 +8869670 +8869332 +8872710 +8872740 +8872722 +8870146 +8872690 +8872676 +8872698 +8869872 +8870028 +8870160 +8870252 +8872748 +8870402 +8870430 +8870512 +8872764 +8870582 +8870496 +8870532 +8870732 +8870608 +8870868 +8870920 +8870950 +8870644 +8870858 +8872804 +8870698 +8870560 +8870662 +8870738 +8870520 +8870916 +8872822 +8872826 +8869936 +8869934 +8870060 +8870140 +8870076 +8870066 +8872058 +8870002 +8872336 +8869944 +8872010 +8869870 +8869946 +8872008 +8872322 +8872324 +8869930 +8872334 +8870030 +8870034 +8872342 +8870072 +8872946 +8870234 +8870378 +8870352 +8870354 +8870386 +8870392 +8870434 +8870538 +8870586 +8870628 +8870612 +8870886 +8870888 +8870836 +8870700 +8870740 +8870736 +8870692 +8870694 +8870850 +8870864 +8870870 +8872910 +8868984 +8872912 +8872908 +8868966 +8868956 +8870724 +8870718 +8872982 +8872984 +8870684 +8872054 +8872994 +8868964 +8868952 +8868954 +8872344 +8872948 +8870866 +8872482 +8872966 +8872972 +8872060 +8872346 +8872328 +8872330 +8872332 +8872976 +8872978 +8872980 +8872928 +8872930 +8872932 +8872934 +8872936 +8872938 +8872940 +8872942 +8872944 +8889448 +8889444 +8891344 +8889450 +8889460 +8889482 +8891362 +8891374 +8889540 +8891416 +8889548 +8889550 +8891410 +8889576 +8889582 +8889592 +8889616 +8889626 +8889650 +8889662 +8889894 +8889838 +8889820 +8889814 +8889816 +8889836 +8889964 +8890024 +8890018 +8890428 +8890436 +8890446 +8890494 +8895652 +8895568 +8898214 +8891482 +8890486 +8891480 +8890540 +8890624 +8890656 +8890698 +8890700 +8890654 +8891848 +8890644 +8890786 +8891502 +8890900 +8890902 +8890914 +8890952 +8890958 +8891010 +8891016 +8891014 +8891886 +8890996 +8891036 +8890992 +8890926 +8891026 +8891072 +8891100 +8891134 +8892382 +8891514 +8895404 +8895448 +8895544 +8895694 +8895680 +8895634 +8895466 +8890538 +8892322 +8892320 +8892328 +8892334 +8891148 +8892338 +8895418 +8898228 +8892340 +8892342 +8892344 +8898198 +8891004 +8891028 +8890968 +8891884 +8891074 +8891508 +8891062 +8891064 +8891124 +8891128 +8891890 +8895450 +8895530 +8897558 +8897556 +8891142 +8891224 +8891244 +8891252 +8895446 +8895388 +8898238 +8898224 +8898236 +8897316 +8898254 +8898264 +8896108 +8898280 +8898322 +8896076 +8891342 +8891904 +8891910 +8889452 +8889458 +8891926 +8889474 +8889504 +8891922 +8889532 +8891408 +8891980 +8891986 +8891988 +8889652 +8892006 +8889672 +8892018 +8891990 +8891434 +8891430 +8891970 +8889810 +8889818 +8890154 +8890212 +8892212 +8892152 +8890264 +8892182 +8892214 +8892110 +8895678 +8895632 +8895582 +8897588 +8895706 +8895650 +8895710 +8895722 +8897314 +8895720 +8896042 +8896002 +8897712 +8895886 +8897672 +8895862 +8895856 +8897666 +8889442 +8889432 +8891522 +8889430 +8889436 +8889480 +8891366 +8891354 +8891394 +8889542 +8891950 +8889594 +8889618 +8889598 +8889590 +8891572 +8889600 +8889636 +8889658 +8889622 +8891364 +8891556 +8890070 +8891710 +8890092 +8890386 +8890218 +8890220 +8890156 +8891744 +8890216 +8890254 +8890336 +8890276 +8890306 +8890384 +8891804 +8891792 +8891790 +8891784 +8891802 +8890046 +8892052 +8889798 +8892040 +8892050 +8892022 +8891442 +8891996 +8892104 +8891458 +8890288 +8890392 +8891472 +8892216 +8890526 +8890460 +8890464 +8890626 +8892276 +8890632 +8890778 +8891484 +8890640 +8892224 +8892220 +8892234 +8892262 +8892268 +8892282 +8890898 +8890892 +8890888 +8890886 +8890998 +8890918 +8890894 +8892346 +8891290 +8891122 +8891282 +8891284 +8891280 +8892336 +8892332 +8892326 +8892324 +8889932 +8889834 +8892068 +8889832 +8889800 +8889846 +8889790 +8889770 +8889768 +8889700 +8889842 +8890014 +8891626 +8890080 +8890370 +8890318 +8890286 +8890316 +8890426 +8891778 +8890492 +8890536 +8891816 +8890474 +8890462 +8890472 +8891826 +8890508 +8891828 +8890658 +8890736 +8890730 +8891856 +8890534 +8891812 +8890544 +8891818 +8891820 +8890542 +8891814 +8890788 +8890766 +8890768 +8890916 +8891044 +8891168 +8891150 +8891896 +8891898 +8891056 +8891162 +8891176 +8891058 +8891888 +8891894 +8891892 +8891170 +8892348 +8891066 +8891144 +8891230 +8891086 +8891008 +8890976 +8890984 +8891286 +8891288 +8891412 +8891276 +8891586 +8891592 +8891632 +8891670 +26325162 +26325160 +26325158 +8891702 +8891684 +8891674 +8891672 +8891668 +8891598 +8891590 +8891588 +8891628 +8891656 +8891666 +8891660 +8891630 +8897548 +8897546 +8897540 +8897534 +8891734 +8891740 +8891750 +8891762 +8891746 +8869928 +8872038 +8869906 +8872032 +8872018 +8869876 +8869866 +8869880 +8872020 +8868624 +8872640 +8870328 +8870178 +8871970 +8869764 +8869716 +8869692 +8869646 +8869562 +8869512 +8872292 +8869456 +8872284 +8869444 +8872286 +8872288 +8869310 +8869284 +8869250 +8869178 +8869058 +8869014 +8868958 +8868898 +8868868 +8868798 +8868778 +8868724 +8868720 +8868682 +8869564 +8869504 +8871950 +8871948 +8871946 +8869322 +8869318 +8869282 +8869276 +8869264 +8869096 +8869266 +8869260 +8870744 +8870758 +8870760 +8870714 +8872082 +8872080 +8870660 +8870630 +8870638 +8870702 +8869736 +8869804 +8869832 +8869844 +8872026 +8870262 +8869248 +8869240 +8869202 +8869142 +8871932 +8869124 +8869092 +8869048 +8869036 +8870246 +8870202 +8870904 +8870326 +8872100 +8870730 +8870522 +8872068 +8870404 +8870324 +8872704 +8869874 +8869868 +8872024 +8869700 +8869496 +8871944 +8871942 +8871938 +8871936 +8869258 +8868936 +8872614 +8872588 +8872570 +8872552 +8872550 +8871918 +8868808 +8868900 +8868840 +8868714 +8872540 +8872548 +8872546 +8868796 +8872524 +8868626 +8872522 +8872532 +8868722 +8868780 +8872580 +8869082 +8869180 +8869224 +8869316 +8869442 +8869432 +8869300 +8869166 +8872610 +8869028 +8872566 +8868856 +8872560 +8869900 +8869714 +8871954 +8869324 +8869396 +8869390 +8872274 +8872276 +8872278 +8869188 +8869216 +8869120 +8869078 +8868978 +8869050 +8872216 +8869004 +8871926 +8872578 +8869144 +8869056 +8869030 +8869032 +8868960 +8868914 +8871922 +8868938 +8872220 +8872218 +8871920 +8868894 +8868892 +8872200 +8872202 +8872198 +8868912 +8868876 +8868810 +8868962 +8868896 +8872554 +8868862 +8868842 +8868774 +8868762 +8868772 +8868742 +8868760 +8868768 +8868764 +8868718 +8868648 +8868662 +8868684 +8868668 +8868642 +8868686 +8868726 +8872194 +8868982 +8869280 +8872282 +8872280 +8869368 +8869320 +8869094 +8869038 +8869086 +8868946 +8868944 +8872586 +8868928 +8868908 +8868910 +8868886 +8868884 +8872646 +8869480 +8869582 +8869560 +8869524 +8872664 +8869566 +8869650 +8869686 +8872694 +8872898 +8869734 +8872028 +8870012 +8871964 +8872746 +8870184 +8870174 +8870230 +8869980 +8870064 +8872724 +8872674 +8869532 +8869530 +8869570 +8870154 +8870348 +8870260 +8870408 +8872758 +8872760 +8872770 +8872774 +8872756 +8870424 +8870704 +8870554 +8870556 +8872806 +8870596 +8870510 +8870508 +8870304 +8870290 +8870422 +8872792 +8870452 +8872796 +8870648 +8870726 +8872820 +8870856 +8870750 +8872830 +8869256 +8869308 +8869312 +8869400 +8869382 +8869454 +8869468 +8869470 +8869474 +8869466 +8869642 +8869648 +8872298 +8869694 +8872302 +8869806 +8869834 +8869838 +8872030 +8869794 +8869840 +8869856 +8869828 +8872310 +8872022 +8872048 +8870070 +8870132 +8870136 +8872042 +8870108 +8872348 +8870170 +8872050 +8872728 +8869878 +8872040 +8870050 +8869726 +8869668 +8869666 +8869624 +8869596 +8872668 +8869622 +8869586 +8869598 +8872294 +8870248 +8870256 +8870294 +8870300 +8870322 +8870332 +8870296 +8870302 +8870398 +8870444 +8870310 +8872078 +8870632 +8870658 +8872480 +8870606 +8870580 +8870490 +8872466 +8870548 +8870482 +8870594 +8870578 +8870568 +8872800 +8870570 +8870450 +8870420 +8870472 +8870486 +8870550 +8870640 +8870710 +8872492 +8870784 +8870932 +8870786 +8870712 +8870822 +8870960 +8870720 +8870852 +8872902 +8870180 +8872904 +8870204 +8868620 +8868606 +8872922 +8868618 +8872918 +8868600 +8872920 +8868602 +8872900 +8872916 +8869350 +8869398 +8869776 +8872268 +8872178 +8872180 +8872182 +8872312 +8872314 +8872316 +8872318 +8872326 +8889782 +8890548 +8889528 +8891568 +8891418 +8891424 +8889572 +8889596 +8889694 +8889696 +8890030 +8890000 +8890004 +8891688 +8889978 +8889980 +8889928 +8889958 +8889992 +8890056 +8890052 +8890246 +8890272 +8890300 +8890414 +8890488 +8890634 +8890856 +8890642 +8890896 +8890882 +8890870 +8891456 +8890152 +8891468 +8890268 +8890312 +8890404 +8890552 +8890600 +8890290 +8889484 +8892016 +8891994 +8892072 +8889684 +8892024 +8891420 +8889664 +8892036 +8892090 +8891454 +8892192 +8890706 +8892302 +8890652 +8892238 +8892250 +8892274 +8890566 +8892206 +8892204 +8892350 +8892156 +8892154 +8892176 +8892196 +8892218 +8890416 +8890490 +8892208 +8892084 +8889558 +8889574 +8891614 +8889726 +8889736 +8891620 +8889570 +8889612 +8891578 +8891580 +8889698 +8890016 +8891646 +8889892 +8890270 +8890256 +8891782 +8891780 +8890590 +8890688 +8890458 +8890430 +8890456 +8890528 +8890592 +8890280 +8890328 +8891798 +8890340 +8890350 +8890348 +8890354 +8891460 +8890144 +8890180 +8890260 +8891788 +8890222 +8890302 +8890274 +8889998 +8889970 +8891644 +8889830 +8889972 +8892092 +8889976 +8892082 +8892088 +8892112 +8889984 +8892096 +8892310 +8892288 +8892286 +8890720 +8890742 +8892312 +8890810 +8890948 +8891024 +8891116 +8889912 +8891658 +8889916 +8889802 +8889804 +8889960 +8889900 +8890022 +8889930 +8889914 +8891676 +8892360 +8891650 +8890790 +8890738 +8890714 +8890708 +8890668 +8890684 +8890850 +8890812 +8891680 +8891692 +8891690 +8891694 +8891736 +8891696 +8891698 +8891724 +8891766 +8891764 +8891756 +8891748 +8891742 +26325144 +26325142 +8891836 +8866454 +8866470 +8866440 +8869070 +8869024 +8871924 +8868924 +8868906 +8868888 +8868874 +8868864 +8868812 +8868802 +8868776 +8868704 +8868694 +8868680 +8868656 +8868640 +8868628 +8868616 +8866460 +8866534 +8866434 +8866432 +8866422 +8869244 +8869226 +8869208 +8869176 +8871930 +8869114 +8869108 +8869098 +8869040 +8868996 +8869002 +8868638 +8868572 +8866476 +8866464 +8866560 +8866458 +8868748 +8868854 +8868826 +8868728 +8868708 +8868698 +8868688 +8868672 +8868634 +8868590 +8868586 +8868580 +8866456 +8868562 +8868784 +8868804 +8868800 +8868788 +8868790 +8868756 +8868922 +8868824 +8868814 +8868700 +8872534 +8872528 +8868604 +8868584 +8872518 +8866472 +8872520 +8872526 +8872530 +8868730 +8866588 +8872544 +8868564 +8866590 +8868596 +8868592 +8868692 +8868678 +8868654 +8868568 +8866576 +8866578 +8866574 +8868710 +8869018 +8868948 +8868904 +8868858 +8880520 +8872208 +8868872 +8872564 +8868712 +8868702 +8868696 +8868582 +8872890 +8872914 +8868636 +8868670 +8868632 +8872184 +8872186 +8868666 +8868690 +8868828 +8868734 +8868676 +8868608 +8866474 +8868594 +8866468 +8868732 +8868574 +8868674 +8872164 +8872166 +8872168 +8866466 +8868610 +8872172 +8866452 +8866448 +8866450 +8866544 +8866444 +8866548 +8866462 +8866420 +8866542 +8866582 +8866580 +8866572 +8868716 +8868786 +8868744 +8872556 +8872574 +8868860 +8868870 +8872558 +8868866 +8872562 +8868836 +8872592 +8868890 +8868934 +8868970 +8869000 +8869100 +8869062 +8872632 +8872636 +8869112 +8872608 +8869012 +8868932 +8868986 +8872604 +8872594 +8872582 +8872600 +8872616 +8872624 +8872628 +8869158 +8869246 +8869288 +8869336 +8872654 +8869366 +8869406 +8866436 +8866438 +8866546 +8866552 +8866550 +8866430 +8866416 +8866410 +8866538 +8866540 +8866442 +8868644 +8868758 +8868822 +8868830 +8872210 +8868834 +8868850 +8868832 +8872212 +8872214 +8868806 +8868846 +8872224 +8872222 +8868844 +8868820 +8872204 +8872206 +8868902 +8868920 +8868878 +8872226 +8868926 +8872228 +8868976 +8868980 +8869072 +8869238 +8869140 +8872246 +8869204 +8869106 +8869010 +8869088 +8869008 +8869006 +8868988 +8868930 +8868994 +8868942 +8868940 +8868992 +8868998 +8869060 +8869064 +8869066 +8869212 +8869242 +8869272 +8869342 +8868560 +8868570 +8872188 +8872190 +8872192 +8866568 +8872170 +8872174 +8866554 +8866556 +8866558 +8889562 +8892782 +8889702 +8889706 +8889778 +8889848 +8892738 +8892740 +8890138 +8890192 +8890214 +8892776 +8890266 +8892786 +8892788 +8892774 +8892226 +8890076 +8890100 +8890130 +8891774 +8891772 +8890182 +8892770 +8890208 +8892772 +8889708 +8892808 +8892756 +8892742 +8889954 +8892066 +8892746 +8892748 +8892752 +8892804 +8892806 +8892750 +8889724 +8889718 +8891616 +8889704 +8892148 +8892184 +8892764 +8892762 +8892778 +8892780 +8892792 +8890412 +8892270 +8892252 +8890344 +8890244 +8890078 +8890132 +8890184 +8891794 +8892768 +8892784 +8890358 +8890394 +8890410 +8892810 +8890442 +8892790 +8890478 +8890406 +8890378 +8891840 +8891838 +8892766 +8892760 +8892758 +8892744 +8892754 +8866400 +8866956 +8868974 +8874072 +8866418 +8866412 +8866408 +8866404 +8872924 +8866482 +8867020 +8867018 +8867010 +8866996 +8866990 +8866984 +8866982 +8866968 +8869020 +8868588 +8868612 +8868650 +8868706 +8868750 +8868752 +8868770 +8868782 +8868794 +8872538 +8868660 +8868622 +8866480 +8874070 +8874084 +8867034 +8867030 +8867072 +8867026 +8866978 +8867066 +8866964 +8866960 +8866958 +8866970 +8867068 +8866994 +8867070 +8867016 +8868614 +8874062 +8874066 +8874064 +8867032 +8867028 +8866980 +8866976 +8866966 +8866962 +8866986 +8866972 +8866974 +8866988 +8866992 +8867046 +8866998 +8867012 +8867004 +8867000 +8867006 +8867014 +8867002 +8867024 +8866428 +8866570 +8866402 +8866398 +8866414 +8866586 +8866426 +8866584 +8872542 +8868658 +8872536 +8868746 +8868754 +8868738 +8868740 +8868848 +8868852 +8872598 +8868916 +8868972 +8867008 +8867022 +8867076 +8866396 +8866536 +8866406 +8866424 +8866446 +8868664 +8868652 +8872196 +8868646 +8868630 +8868766 +8868918 +8868792 +8868818 +8874074 +8868816 +8874076 +8868838 +8872892 +8866478 +8868576 +8868578 +8872926 +26325244 +8872176 +8866562 +8866564 +166755071 +166755072 +8867048 +8867050 +8867052 +8867054 +8867056 +8867058 +8867060 +8867062 +8867064 +8867074 +166755073 +166755074 \ No newline at end of file From 8816fcf30043e34188b5de7601fbb7114eba44a0 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 4 Dec 2020 17:20:42 -0500 Subject: [PATCH 08/54] fixed SettingWithCopyWarning in merge_parameters --- .../troute/nhd_network_augment.py | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_augment.py b/src/python_framework_v02/troute/nhd_network_augment.py index 989a58bc9..4bb6a9edf 100644 --- a/src/python_framework_v02/troute/nhd_network_augment.py +++ b/src/python_framework_v02/troute/nhd_network_augment.py @@ -370,8 +370,10 @@ def merge_parameters(to_merge): """ data_replace = to_merge.tail(1) + data_replace._is_copy = None idx = to_merge.tail(1).index + data_replace.loc[idx, "Length"] = to_merge.Length.sum() data_replace.loc[idx, "n"] = len_weighted_av(to_merge, "n", "Length") data_replace.loc[idx, "nCC"] = len_weighted_av(to_merge, "nCC", "Length") @@ -384,7 +386,7 @@ def merge_parameters(to_merge): data_replace.loc[idx, "MusK"] = len_weighted_av(to_merge, "MusK", "Length") data_replace.loc[idx, "MusX"] = len_weighted_av(to_merge, "MusX", "Length") data_replace.loc[idx, "ChSlp"] = len_weighted_av(to_merge, "ChSlp", "Length") - + return data_replace def correct_reach_connections(data_merged): @@ -533,19 +535,18 @@ def update_network_data(data, rch, data_merged, chop, rconn): def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_segments, network_data): - if bool(list(pruned_segments)): - + # build a list of all segments that need crosswalking + if bool(list(pruned_segments)): segments = merged_segments + list(pruned_segments) else: - segments = merged_segments - # compute connections + # compute connections using native network data conn = nhd_network.extract_connections(data_native, network_data["columns"]["downstream"]) + rconn = nhd_network.reverse_network(conn) - # initialize a library to store qlat desinations for pruned/merged segments -# qlat_destinations = {x: {} for x in str(segments)} + # initialize a dictionary to store qlat destination nodes for pruned/merged segments qlat_destinations = {} for idx in segments: @@ -553,19 +554,24 @@ def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_s # get the downstream connection of this segment in the original network ds_idx = conn[idx] - # find the nearest downstream segment remaining in the pruned/merged network - while bool(ds_idx[0] in data_merged.index) == False: - ds_idx = conn[ds_idx[0]] + # find the segment to recieve qlats from the pruned or merged segment + if ds_idx: + # if a downstream connection exists, find the nearest one reamining + while bool(ds_idx[0] in data_merged.index) == False: + ds_idx = conn[ds_idx[0]] + else: + # TO DO: this method simply discards lateral inflows for tailwater segs, need to find a destination !! + ds_idx = [] # update the qlat destination dict - qlat_destinations[str(idx)] = str(ds_idx[0]) + qlat_destinations[str(idx)] = str(ds_idx) return qlat_destinations def segment_merge(data_native, data, network_data, thresh, pruned_segments): # create a copy of the pruned network dataset, which will be updated with merged data - data_merged = data.copy() + data_merged = data.copy() # !! this line may be throwing a warning # initialize list to store merged segment IDs merged_segments = [] @@ -636,7 +642,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # upstream merge chop = [] - reach_merged, chop = upstream_merge(reach_merged, chop) + reach_merged, chop = upstream_merge(reach_merged, chop) # this is thowing a warning # update chop_reach list with merged-out segments chop_reach.extend(chop) @@ -697,7 +703,7 @@ def main(): # prune headwaters if prune and snap: filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.nc" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.json" print("Prune, snap, then merge:") print("pruning headwaters...") @@ -720,7 +726,7 @@ def main(): if snap and not prune: filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.nc" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.json" print("Snap and merge:") print("snapping junctions...") @@ -737,7 +743,7 @@ def main(): if not snap and prune: filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.nc" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.json" print("Prune and merge:") print("snapping junctions...") @@ -754,7 +760,7 @@ def main(): if not snap and not prune: filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge.nc" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.nc" + filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.json" print("Just merge:") print("merging segments...") data_merged, qlat_destinations = segment_merge( @@ -766,12 +772,8 @@ def main(): ) # update RouteLink data - RouteLink_edit = RouteLink.loc[data_merged.index.values] - - print(RouteLink_edit.head()) - + RouteLink_edit = RouteLink.loc[data_merged.index.values] for (columnName, columnData) in data_merged.iteritems(): - print(columnName) RouteLink_edit.loc[:,columnName] = columnData for idx in RouteLink_edit.index: @@ -782,15 +784,12 @@ def main(): print("exporting RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) DS = xr.Dataset.from_dataframe(RouteLink_edit) DS.to_netcdf(os.path.join(root, "test", "input", "geo", "Channels", filename)) - - print(qlat_destinations) - + # save cross walk as json print("exporting CrossWalk file:", filename_cw, "to", os.path.join(root, "test", "input", "geo", "Channels")) with open(os.path.join(root, "test", "input", "geo", "Channels", filename_cw), "w") as outfile: json.dump(qlat_destinations, outfile) - if __name__ == "__main__": main() From 4f03a1cd6754bacc35ab3077a4d4dc308ae7e64e Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 11 Dec 2020 10:20:12 -0500 Subject: [PATCH 09/54] export RouteLinks as shapefiles --- .../troute/nhd_network_augment.py | 134 ++++++++++++------ 1 file changed, 91 insertions(+), 43 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_augment.py b/src/python_framework_v02/troute/nhd_network_augment.py index 4bb6a9edf..f01d3fae2 100644 --- a/src/python_framework_v02/troute/nhd_network_augment.py +++ b/src/python_framework_v02/troute/nhd_network_augment.py @@ -1,5 +1,6 @@ import numpy as np import pandas as pd +import geopandas as gpd import xarray as xr from functools import partial from itertools import chain @@ -8,6 +9,8 @@ import pathlib import argparse import json +from tqdm import tqdm +import time root = pathlib.Path("../../../").resolve() sys.path.append(os.path.join(root, "src", "python_framework_v01")) @@ -45,7 +48,8 @@ def _handle_args(): "Mainstems_CONUS", "CONUS_Named_Streams", "CONUS_FULL_RES_v20", - "CapeFear_FULL_RES" + "CapeFear_FULL_RES", + "Florence_FULL_RES" ], default="CapeFear_FULL_RES", ) @@ -63,6 +67,13 @@ def _handle_args(): dest = "snap", action = "store_true" ) + parser.add_argument( + "-return_original", + "--return_original", + help = "return an unmodified RouteLink.nc file for the specified domain", + dest = "return_original", + action = "store_true" + ) return parser.parse_args() @@ -84,8 +95,7 @@ def get_network_data(network_name): network_dl.download(network_data["geo_file_path"], network_data["data_link"]) # read-in NHD data, retain copies for viz- and full network analysis purposes - data = nhd_io.read(network_data["geo_file_path"]) - RouteLink = data.copy() # copy of full resolution CONUS network + RouteLink = nhd_io.read(network_data["geo_file_path"]) # select only the necessary columns of geospatial data, set the DataFrame index cols = [v for c, v in network_data["columns"].items()] @@ -285,20 +295,18 @@ def snap_junctions(data, threshold, network_data): # build list of headwater segments hws = connections.keys() - chain.from_iterable(connections.values()) - + + print("finding non-headwater reaches") # create a list of short reaches stranded between junctions - short_reaches = [] - for sublists in list(subreaches.values()): - for rch in sublists: + short_reaches = () + for sublists in tqdm(list(subreaches.values())): + for rch in sublists: + head = rch[0] + # if reach is not a headwater - if len(np.setdiff1d(rch,list(hws))) == len(rch): - - # and if reach is shorter than threshold length - if data.loc[rch,"Length"].sum() < threshold: - - # consider it for junction snapping - short_reaches.append(rch) + if rconn[head] and data.loc[rch,"Length"].sum() < threshold: + short_reaches += tuple(rch) if iter_num > 1: print("After iteration", iter_num - 1, ",", len(short_reaches), "short reaches remain") @@ -313,8 +321,13 @@ def snap_junctions(data, threshold, network_data): for i, rch in enumerate(short_reaches): # identify reach tail (downstream-most) and head (upstream-most) segments - tail = rch[-1] - head = rch[0] + + if type(rch) is tuple: + tail = rch[-1] + head = rch[0] + else: + tail = rch + head = rch # identify segments that drain to reach head us_conn = rconn[head] @@ -515,17 +528,14 @@ def update_network_data(data, rch, data_merged, chop, rconn): data (DataFrame): Updated network routing parameters """ - # make a copy of the data to be replaced with merged - data_old = data.loc[rch].copy() - # drop the segments that disapeared with merger data = data.drop(chop) - + # adjust the segment data for those that remain data.loc[data_merged.index] = data_merged # update out of reach connections - these will change in the first segment was merged out - upstreams = rconn[data_old.head(1).index.values[0]] # upstream connection of the OLD reach head + upstreams = rconn[rch[0]] # upstream connection of the OLD reach head if bool(upstreams): @@ -551,16 +561,18 @@ def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_s for idx in segments: - # get the downstream connection of this segment in the original network - ds_idx = conn[idx] - # find the segment to recieve qlats from the pruned or merged segment - if ds_idx: - # if a downstream connection exists, find the nearest one reamining + if conn[idx]: + ds_idx = conn[idx] while bool(ds_idx[0] in data_merged.index) == False: ds_idx = conn[ds_idx[0]] + + elif rconn[idx]: + ds_idx = rconn[idx] + while bool(ds_idx[0] in data_merged.index) == False: + us_idx = conn[ds_idx[0]] + else: - # TO DO: this method simply discards lateral inflows for tailwater segs, need to find a destination !! ds_idx = [] # update the qlat destination dict @@ -570,8 +582,10 @@ def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_s def segment_merge(data_native, data, network_data, thresh, pruned_segments): - # create a copy of the pruned network dataset, which will be updated with merged data - data_merged = data.copy() # !! this line may be throwing a warning + + + # create a copy of the pruned network dataset, which will be updated with merged data + data_merged = data.copy() # initialize list to store merged segment IDs merged_segments = [] @@ -586,10 +600,9 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): for twi, (tw, rchs) in enumerate(subreaches.items(), 1): for rch in rchs: - - # calculate reach length + rch_len = data.loc[rch].Length.sum() - + ################################################## # orphaned short single segment reaches ################################################## @@ -642,7 +655,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # upstream merge chop = [] - reach_merged, chop = upstream_merge(reach_merged, chop) # this is thowing a warning + reach_merged, chop = upstream_merge(reach_merged, chop) # update chop_reach list with merged-out segments chop_reach.extend(chop) @@ -659,6 +672,7 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # update chop_reach list with merged-out segments chop_reach.extend(chop) + # correct segment connections within reach reach_merged = correct_reach_connections(reach_merged) @@ -686,23 +700,24 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): def main(): + # unpack command line arguments args = _handle_args() - supernetwork = args.supernetwork threshold = args.threshold prune = args.prune snap = args.snap + return_original = args.return_original # get network data print("Extracting and organizing supernetwork data") data, RouteLink, network_data = get_network_data(supernetwork) RouteLink = RouteLink.set_index(network_data["columns"]["key"]) - + pruned_segs = [] - # prune headwaters if prune and snap: - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.nc" + dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge" + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.shp" filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.json" print("Prune, snap, then merge:") @@ -725,7 +740,8 @@ def main(): ) if snap and not prune: - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.nc" + dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge" + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.shp" filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.json" print("Snap and merge:") @@ -742,7 +758,8 @@ def main(): ) if not snap and prune: - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.nc" + dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge" + filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.shp" filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.json" print("Prune and merge:") @@ -759,6 +776,7 @@ def main(): ) if not snap and not prune: + dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge" filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge.nc" filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.json" print("Just merge:") @@ -773,6 +791,7 @@ def main(): # update RouteLink data RouteLink_edit = RouteLink.loc[data_merged.index.values] + for (columnName, columnData) in data_merged.iteritems(): RouteLink_edit.loc[:,columnName] = columnData @@ -780,15 +799,44 @@ def main(): if RouteLink_edit.loc[idx,'to'] < 0: RouteLink_edit.loc[idx,'to'] = 0 - # export as NetCDF + # convert RouteLink to geodataframe + RouteLink_edit = gpd.GeoDataFrame(RouteLink_edit, geometry=gpd.points_from_xy(RouteLink_edit.lon, RouteLink_edit.lat)) + + # export merged data print("exporting RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) - DS = xr.Dataset.from_dataframe(RouteLink_edit) - DS.to_netcdf(os.path.join(root, "test", "input", "geo", "Channels", filename)) + + dir_path = os.path.join(root, "test", "input", "geo", "Channels", dirname) + if not os.path.isdir(dir_path): + os.mkdir(dir_path) + # save RouteLink data as shapefile + RouteLink_edit = RouteLink_edit.drop(columns = ['time', 'gages']) + RouteLink_edit.to_file(os.path.join(dir_path,filename)) + # save cross walk as json print("exporting CrossWalk file:", filename_cw, "to", os.path.join(root, "test", "input", "geo", "Channels")) - with open(os.path.join(root, "test", "input", "geo", "Channels", filename_cw), "w") as outfile: + with open(os.path.join(dir_path, filename_cw), "w") as outfile: json.dump(qlat_destinations, outfile) + + # export original data + if return_original: + dirname = "RouteLink_" + supernetwork + filename = "RouteLink_" + supernetwork + ".shp" + print("exporting unmodified RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) + + dir_path = os.path.join(root, "test", "input", "geo", "Channels", dirname) + if not os.path.isdir(dir_path): + os.mkdir(dir_path) + + RouteLink_domain = RouteLink.loc[data.index.values] + RouteLink_domain = gpd.GeoDataFrame(RouteLink_domain, geometry=gpd.points_from_xy(RouteLink_domain.lon, RouteLink_domain.lat)) + + RouteLink_domain = RouteLink_domain.drop(columns = ['time', 'gages']) + RouteLink_domain.to_file(os.path.join(dir_path,filename)) + + print("Number of segments in modified RouteLink:", len(RouteLink_edit)) + print("Number of segments in original RouteLink:", len(RouteLink_domain)) + if __name__ == "__main__": main() From 2d2a273222c0fcbd92c8a441d0f45eee7bdabf3b Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 11 Dec 2020 10:22:47 -0500 Subject: [PATCH 10/54] added Florence Test domain, catchment above Falls Lake, as supernetwork option --- .../troute/nhd_network_utilities_v02.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 677321886..0292973f5 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -24,6 +24,7 @@ def set_supernetwork_data( "CONUS_Named_Streams", "CONUS_FULL_RES_v20", "CapeFear_FULL_RES", + "Florence_FULL_RES", "custom", } @@ -308,6 +309,27 @@ def set_supernetwork_data( ) return rv + elif supernetwork == "Florence_FULL_RES": + rv = set_supernetwork_data( + supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder + ) + rv.update( + { + "title_string": "Hurricane Florence Domain, near Durham NC", # overwrites other title... + "mask_file_path": os.path.join( + geo_input_folder, + "Channels", + "masks", + "Florence_FULL_RES.txt", + ), + "mask_driver_string": "csv", + "mask_layer_string": "", + "mask_key": 0, + "mask_name": 1, # TODO: Not used yet. + } + ) + return rv + elif supernetwork == "CONUS_FULL_RES_v20": ROUTELINK = "RouteLink_NHDPLUS" From a2cda662a19b90e4b9970762ab997d4ff7213a84 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 11 Dec 2020 10:24:04 -0500 Subject: [PATCH 11/54] mask for catchment above Falls Lake, Florence Test domain --- .../geo/Channels/masks/Florence_FULL_RES.txt | 1108 +++++++++++++++++ 1 file changed, 1108 insertions(+) create mode 100644 test/input/geo/Channels/masks/Florence_FULL_RES.txt diff --git a/test/input/geo/Channels/masks/Florence_FULL_RES.txt b/test/input/geo/Channels/masks/Florence_FULL_RES.txt new file mode 100644 index 000000000..bdabb8395 --- /dev/null +++ b/test/input/geo/Channels/masks/Florence_FULL_RES.txt @@ -0,0 +1,1108 @@ +8777733 +8777735 +8777737 +8777739 +8777741 +8777749 +8777755 +8777765 +8777767 +8777769 +8777773 +8777777 +8777785 +8777793 +8777795 +8777797 +8777799 +8777805 +8777807 +8777809 +8777811 +8777813 +8777819 +8777821 +8777825 +8777827 +8777829 +8777833 +8777835 +8777839 +8777841 +8777849 +8777851 +8777853 +8777855 +8777861 +8777863 +8777867 +8777869 +8777875 +8777879 +8777881 +8777885 +8777887 +8777895 +8777897 +8777899 +8777905 +8777907 +8777921 +8777923 +8777925 +8777929 +8777931 +8780643 +8777935 +8777937 +8777941 +8777943 +8777945 +8777949 +8777951 +8777955 +8777961 +8777963 +8777967 +8777979 +8777987 +8777991 +8780655 +8777997 +8778001 +8778007 +8778009 +8778011 +8778013 +8778015 +8778021 +8778023 +8778025 +8778027 +8778031 +8778033 +8778041 +8778043 +8778045 +8778049 +8778053 +8778055 +8778061 +8778065 +8778079 +8778083 +8778085 +8778095 +933020016 +933020017 +933020018 +933020020 +8778101 +8778103 +933020024 +8778105 +933020023 +933020025 +933020028 +933020029 +933020027 +8778111 +933020032 +8778113 +933020035 +933020036 +8778117 +8778115 +8778119 +933020041 +8778127 +933020048 +8778129 +933020050 +933020053 +933020058 +933020059 +933020061 +8778141 +8778143 +8778151 +8778153 +8778155 +933020083 +933020089 +933020090 +933020091 +933020092 +8778169 +8776621 +8778185 +8780233 +8780235 +933020109 +933020110 +8780239 +8780237 +8778193 +8780241 +933020115 +8780243 +933020117 +8780245 +8778199 +8780247 +8780249 +8780251 +8780253 +933020126 +8780255 +8776627 +8780259 +8780261 +8780263 +8780265 +8780267 +8780269 +8780271 +8780273 +8776631 +8780275 +8780277 +8780279 +8780281 +8780283 +8780285 +8780287 +933020161 +933020162 +8780289 +8780291 +8780293 +8776635 +8780295 +8778249 +8780297 +8780299 +8780301 +8780303 +8776637 +8780305 +8780307 +8778261 +8780309 +8780311 +8780313 +8780315 +8780317 +8780319 +8780321 +8780323 +8780325 +8780327 +8780329 +8780331 +8780333 +8776643 +8780335 +8780337 +8780339 +8780341 +8780343 +8778297 +8780345 +8778299 +8780347 +8778301 +8780349 +8782399 +8780351 +8780353 +8780773 +8780355 +8780357 +8780359 +8780361 +8780363 +8780365 +8780367 +8780369 +8780371 +8780721 +8780373 +8780375 +8778329 +8780377 +8778331 +8780379 +8778333 +8780381 +8778335 +8780383 +8778337 +8780385 +8778339 +8780387 +8778341 +8780389 +8778343 +8780725 +8778345 +8780391 +8778347 +8780395 +8778349 +8780397 +8778351 +8780399 +8778353 +8780401 +8778355 +8780403 +8778357 +8780405 +8778359 +8780407 +8778361 +8780409 +8778363 +8780411 +8778365 +8780413 +8778367 +8780415 +8778369 +8780417 +8778371 +8780419 +8778373 +8780421 +8780423 +8778377 +8780425 +8778379 +8780427 +8780429 +8778383 +8780431 +8778385 +8780433 +8778387 +8780435 +8780437 +8780439 +8780441 +8780735 +8780443 +8780445 +8780447 +8780777 +8780449 +8780451 +8780737 +8780453 +8780455 +8780457 +8780459 +8780461 +8780739 +8780463 +8780465 +8780467 +8780469 +8778423 +8780471 +8778425 +8780473 +8778427 +8780475 +8778429 +8780477 +8778431 +8780479 +8778433 +8780481 +8778435 +8780483 +8778437 +8780485 +8778439 +8780487 +8778441 +8780489 +8778443 +8780491 +8778445 +8780493 +8778447 +8780495 +8778449 +8780497 +8778451 +8780499 +8778453 +8780501 +8778455 +8780503 +8778457 +8780505 +8778459 +8780507 +8778461 +8780509 +8778463 +8780511 +8778465 +8780513 +8778467 +8780515 +8778469 +8780517 +8778471 +8780519 +8778473 +8780521 +8778475 +8780523 +8778477 +8780525 +8778479 +8780527 +8778481 +8780529 +8778483 +8780531 +8780533 +8780753 +8778487 +8780535 +8778489 +8776683 +8778491 +8780539 +8778493 +8780541 +8778495 +8780537 +8778497 +8780543 +8780547 +8780545 +8780549 +8778503 +8780551 +8780553 +8780757 +8780555 +8778509 +8780557 +8778511 +8780559 +8778513 +8780561 +8778515 +8780563 +8780565 +8780759 +8780567 +8780569 +8780571 +8780761 +8778525 +8780573 +8778527 +8780575 +8780577 +8778531 +8780579 +8780581 +8780763 +8780583 +8780585 +8780587 +8778541 +8780589 +8780591 +8778545 +8780593 +8778547 +8776695 +8780595 +8780597 +8780599 +8780601 +8780603 +8780605 +8778559 +8780607 +8780609 +8778563 +8780611 +8778565 +8780613 +8778567 +8780769 +8780617 +8780615 +8780619 +8778571 +8778573 +8780621 +8778575 +8780623 +8778577 +8780625 +8780771 +8780627 +8778581 +8780629 +8778583 +8780631 +8778585 +8780633 +8778587 +8780635 +8778589 +8780637 +8780639 +8778591 +8778593 +8780641 +8778595 +8780775 +8778597 +8776705 +8780647 +8780645 +8780649 +8778599 +8780651 +8778603 +8780653 +8778605 +8778607 +8778601 +8780657 +8778609 +8778611 +8780659 +8780661 +8778613 +8780663 +8778615 +8778617 +8780665 +8778619 +8778621 +8778623 +8778625 +8778627 +8778629 +8778631 +8778633 +8778635 +8780783 +8778637 +8778639 +8778641 +8778643 +8778645 +8780785 +8778647 +8778649 +8778651 +8778653 +8778655 +8780787 +8778657 +8778659 +8778661 +8778663 +8778665 +8780789 +8778667 +8776619 +8778669 +8780717 +8778671 +8776623 +8778673 +8776625 +8778675 +8780719 +8778677 +8776629 +8780727 +8778679 +8778681 +8780729 +8778683 +8780731 +8778685 +8780733 +8778687 +8780793 +8778689 +8776633 +8778691 +8776639 +8778693 +8776645 +8778695 +8776641 +8778697 +8776649 +8776651 +8776647 +8778701 +8776653 +8776655 +8780751 +8778705 +8776657 +8776659 +8780755 +8778709 +8776661 +8778711 +8776663 +8778713 +8776665 +8778715 +8776667 +8780765 +8776669 +8778719 +8776671 +8778721 +8776673 +8778723 +8780767 +8778725 +8776675 +8778727 +8776679 +8778729 +8776681 +8776677 +8780779 +8778733 +8776685 +8778735 +8776687 +8776689 +8780781 +8778739 +8776691 +8778741 +8776693 +8778743 +8780791 +8778745 +8776697 +8778747 +8776699 +8778749 +8776701 +8778751 +8776703 +8778753 +8780799 +8778755 +8780803 +8776709 +8780797 +8776711 +8776707 +8778761 +8780809 +8778763 +8776713 +8778765 +8776715 +8780811 +8776719 +8778769 +8776721 +8778771 +8776723 +8776725 +8776717 +8780823 +8776727 +8776729 +8780825 +8776731 +8780819 +8776733 +8780829 +8780831 +8776735 +8776737 +8780813 +8778787 +8776739 +8776741 +8780835 +8776743 +8780839 +8776745 +8780837 +8778795 +8776747 +8776749 +8780845 +8776751 +8780847 +8778801 +8776753 +8780851 +8776755 +8780853 +8776757 +8776759 +8780849 +8776761 +8780817 +8778811 +8776763 +8778813 +8776765 +8776767 +8776769 +8776771 +8776773 +8778823 +8776775 +8776777 +8780821 +8776779 +8778829 +8776781 +8778831 +8776783 +8776785 +8778835 +8776787 +8778837 +8776789 +8778839 +8776791 +8776793 +8778843 +8776795 +8778845 +8776797 +8776799 +8778849 +8776801 +8776803 +8780795 +8776805 +8776807 +8780827 +8776809 +8778859 +8776811 +8776813 +8776815 +8778865 +8776817 +8778867 +8776819 +8778869 +8776821 +8776823 +8776825 +8778875 +8776827 +8778877 +8776829 +8778879 +8776831 +8778881 +8776833 +8778883 +8776835 +8776837 +8778887 +8776839 +8778889 +8776841 +8778891 +8776843 +8778893 +8776845 +8776847 +8780833 +8776849 +8778899 +8776851 +8776853 +8778901 +8778903 +8776855 +8778905 +8776857 +8778907 +8776859 +8776861 +8776863 +8776865 +8776867 +8776869 +8776871 +8776873 +8778925 +8776877 +8778927 +8776879 +8778929 +8776881 +8776883 +8780841 +8776885 +8776887 +8776889 +8780843 +8778939 +8776891 +8776893 +8778943 +8776895 +8778945 +8776897 +8778947 +8776899 +8778949 +8776901 +8776903 +8778953 +8776905 +8778955 +8776907 +8778957 +8776909 +8778959 +8776911 +8776913 +8778963 +8776915 +8778965 +8776917 +8778967 +8776919 +8778969 +8776921 +8778971 +8776923 +8778973 +8776925 +8778975 +8776927 +8776929 +8778979 +8776931 +8778981 +8776933 +8776935 +8780393 +8778985 +8776937 +8778987 +8776939 +8776941 +8778989 +8776943 +8778991 +8776945 +8778993 +8776947 +8778995 +8778997 +8776949 +8778999 +8776951 +8779001 +8776953 +8776955 +8779003 +8776957 +8779005 +8776959 +8780801 +8776961 +8776963 +8776965 +8776967 +8776969 +8776971 +8776973 +8776975 +8776977 +8776979 +8776981 +8776983 +8776985 +8776987 +8776989 +8776991 +8776993 +8776995 +8776997 +8776999 +8777001 +8777003 +8777005 +8777007 +8777009 +8777011 +8777013 +8777015 +8777017 +8777019 +8777021 +8777023 +8777025 +8777027 +8777029 +8777031 +8777033 +8777035 +8777037 +8777039 +8780723 +8777041 +8777043 +8777045 +8777047 +8777049 +8777051 +8777053 +8777055 +8777057 +8780805 +8777059 +8777061 +8777063 +8777065 +8777067 +8777069 +8777071 +8777073 +8777075 +8777077 +8777079 +8777081 +8777083 +8777085 +8777087 +8777089 +8777091 +8777093 +8777095 +8777097 +8777099 +8777101 +8777103 +8777105 +8777107 +8780807 +8777109 +8777111 +8777113 +8777115 +8777117 +8777119 +8777121 +8777123 +8777125 +8777127 +8777133 +8777135 +8777137 +8777139 +8777141 +8777155 +8777157 +8777161 +8777163 +8777165 +8777167 +8777169 +8777173 +8777175 +8777177 +8777179 +8777181 +8777183 +8777187 +8777191 +8777193 +8777199 +8777201 +8777203 +8777205 +8777207 +8777209 +8777211 +8777217 +8777219 +8777221 +8777223 +8777225 +8777229 +8777235 +8777237 +8777239 +8777241 +8777243 +8777245 +8777247 +8777253 +8777267 +8777269 +8777271 +8777275 +8777277 +8777285 +8777289 +8777291 +8777297 +8777299 +8777305 +8777307 +8780815 +8777309 +8777311 +8777313 +8777315 +8777317 +8777321 +8777329 +8777331 +8777339 +8777341 +8777343 +8777345 +8777351 +8777353 +8777359 +8777369 +8777371 +8777373 +8777377 +8777379 +8777381 +8777383 +8777385 +8777387 +8777389 +8777391 +8777393 +8777403 +8777409 +8777411 +8777413 +8777415 +8777417 +8777419 +8777425 +8777427 +8777437 +8777439 +8777441 +8777443 +8777449 +8777451 +8777453 +8777455 +8777467 +8777469 +8777473 +8777475 +8777477 +8777479 +8777481 +8777483 +8777485 +8777489 +166737685 +166737686 +166737687 +8780741 +166737689 +166737690 +166737691 +166737692 +166737693 +166737696 +166737697 +8777505 +166737699 +166737701 +8777509 +166737703 +166737704 +166737705 +166737706 +166737707 +8777511 +166737711 +166737712 +166737713 +166737714 +166737715 +8777523 +8777529 +8777533 +8777535 +8777537 +8777541 +8780743 +8777553 +8777555 +8777565 +8777579 +8777591 +8777595 +8780745 +8777597 +8777599 +8777601 +8777605 +8777611 +26023819 +8777613 +8777615 +8777617 +8777627 +8777629 +8777631 +8777633 +8777639 +8777641 +8777643 +8780747 +8777649 +8777653 +8777655 +8777657 +8777663 +8777665 +8777667 +8777669 +8777671 +8777673 +8777677 +8777683 +8777685 +8777687 +8780749 +8777701 +8777703 +8777707 +8777709 +8777711 +8777713 +8777719 +8777725 +8777727 From b3c580427335bd8a9e1165fd449fd6fcd4f833ce Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 11 Dec 2020 10:49:57 -0600 Subject: [PATCH 12/54] Add new network options to main compute script The following basins were added as options to the v02 nhd_network_utilities script as part of PR #177, introducing ability to prepared modified networks. In addition to the new network-modification script, these networks are now in the main compute script: + "CapeFear_FULL_RES", + "Florence_FULL_RES" --- src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 8f8c5d496..cbce2274d 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -116,6 +116,8 @@ def _handle_args(): "Mainstems_CONUS", "CONUS_Named_Streams", "CONUS_FULL_RES_v20", + "CapeFear_FULL_RES", + "Florence_FULL_RES" ], # TODO: accept multiple or a Path (argparse Action perhaps) # action='append', From 9976b84e61a1014c560d2b9033ba901f79d343a1 Mon Sep 17 00:00:00 2001 From: Jacob Hreha Date: Thu, 3 Dec 2020 13:08:18 -0600 Subject: [PATCH 13/54] adding warm states --- .../compute_nhd_routing_SingleSeg_v02.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index cbce2274d..1db670ba1 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -125,6 +125,11 @@ def _handle_args(): dest="supernetwork", default="Pocono_TEST1", ) + parser.add_argument( + "--wrf_hydro_channel_restart_file", + dest="wrf_hydro_channel_restart_file", + help="provide a WRF-Hydro channel warm state file (may be the same as waterbody restart file)", + ) ql_arg_group = parser.add_mutually_exclusive_group() ql_arg_group.add_argument( "--qlc", @@ -304,6 +309,73 @@ def main(): 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" ) + flowveldepth = { + connection: np.zeros(np.array([nts, 10])) + for connection in (network["all_segments"]) + } + flowveldepth_connect = ( + {} + ) # dict to contain values to transfer from upstream to downstream networks + # TODO: generalize with a direction flag + def compute_reach_upstream_flows( + flowveldepth_connect, + flowveldepth, + head_segment=None, + reach=None, + network=None, + supernetwork_parameters=None, + waterbody=None, + ts=0, + dt=60, + verbose=False, + debuglevel=0, + assume_short_ts=False, + ): + global connections + global channel_initial_states_df + + # upstream flow per reach + qup = 0.0 + quc = 0.0 + ######################## + # if waterbody: + # upstreams_list = set() + # for rr in network["receiving_reaches"]: + # for us in connections[rr]["upstreams"]: + # upstreams_list.add(us) + # # this step was critical -- there were receiving reaches that were junctions + # # with only one of their upstreams out of the network. The other was inside + # # the network, so it caused a lookup error. + # upstreams_list = upstreams_list - network["all_segments"] + # us_flowveldepth = flowveldepth_connect + + # elif head_segment in network["receiving_reaches"]: + # # TODO: confirm this logic, to make sure we don't double count the head + # upstreams_list = connections[reach["reach_head"]]["upstreams"] + # us_flowveldepth = flowveldepth + # us_flowveldepth.update(flowveldepth_connect) + + # else: + # upstreams_list = connections[reach["reach_head"]]["upstreams"] + # us_flowveldepth = flowveldepth + + for us in upstreams_list: + if us != supernetwork_parameters["terminal_code"]: # Not Headwaters + quc += us_flowveldepth[us][ts][flowval_index] + + if ts == 0: + # Initialize qup from warm state array + qup += channel_initial_states_df.loc[us, "qd0"] + else: + qup += us_flowveldepth[us][ts - 1][flowval_index] + + if assume_short_ts: + quc = qup + + return quc, qup + + compute_reach_upstream_flows() + if verbose: print("supernetwork connections set complete") if showtiming: From 15a060156d61455a756105b995ad22dca7a9070e Mon Sep 17 00:00:00 2001 From: Jacob Hreha Date: Thu, 3 Dec 2020 15:11:27 -0600 Subject: [PATCH 14/54] hooked up warm states inputs --- .../compute_nhd_routing_SingleSeg_v02.py | 121 +++++++----------- 1 file changed, 45 insertions(+), 76 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 1db670ba1..c98826990 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -248,7 +248,7 @@ def main(): # qlat_file_index_col = forcing_parameters.get("qlat_file_index_col", None) # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) # else: - + wrf_hydro_channel_restart_file = args.wrf_hydro_channel_restart_file # TODO: uncomment custominput file qlat_const = float(args.qlat_const) qlat_input_folder = args.qlat_input_folder @@ -309,73 +309,6 @@ def main(): 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" ) - flowveldepth = { - connection: np.zeros(np.array([nts, 10])) - for connection in (network["all_segments"]) - } - flowveldepth_connect = ( - {} - ) # dict to contain values to transfer from upstream to downstream networks - # TODO: generalize with a direction flag - def compute_reach_upstream_flows( - flowveldepth_connect, - flowveldepth, - head_segment=None, - reach=None, - network=None, - supernetwork_parameters=None, - waterbody=None, - ts=0, - dt=60, - verbose=False, - debuglevel=0, - assume_short_ts=False, - ): - global connections - global channel_initial_states_df - - # upstream flow per reach - qup = 0.0 - quc = 0.0 - ######################## - # if waterbody: - # upstreams_list = set() - # for rr in network["receiving_reaches"]: - # for us in connections[rr]["upstreams"]: - # upstreams_list.add(us) - # # this step was critical -- there were receiving reaches that were junctions - # # with only one of their upstreams out of the network. The other was inside - # # the network, so it caused a lookup error. - # upstreams_list = upstreams_list - network["all_segments"] - # us_flowveldepth = flowveldepth_connect - - # elif head_segment in network["receiving_reaches"]: - # # TODO: confirm this logic, to make sure we don't double count the head - # upstreams_list = connections[reach["reach_head"]]["upstreams"] - # us_flowveldepth = flowveldepth - # us_flowveldepth.update(flowveldepth_connect) - - # else: - # upstreams_list = connections[reach["reach_head"]]["upstreams"] - # us_flowveldepth = flowveldepth - - for us in upstreams_list: - if us != supernetwork_parameters["terminal_code"]: # Not Headwaters - quc += us_flowveldepth[us][ts][flowval_index] - - if ts == 0: - # Initialize qup from warm state array - qup += channel_initial_states_df.loc[us, "qd0"] - else: - qup += us_flowveldepth[us][ts - 1][flowval_index] - - if assume_short_ts: - quc = qup - - return quc, qup - - compute_reach_upstream_flows() - if verbose: print("supernetwork connections set complete") if showtiming: @@ -407,6 +340,42 @@ def compute_reach_upstream_flows( param_df = param_df.astype("float32") # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] + + # STEP 4: Handle Channel Initial States + if showtiming: + start_time = time.time() + if verbose: + print("setting channel initial states ...") + + if wrf_hydro_channel_restart_file: + + channel_initial_states_df = nhd_io.get_stream_restart_from_wrf_hydro( + wrf_hydro_channel_restart_file, + wrf_hydro_channel_ID_crosswalk_file, + wrf_hydro_channel_ID_crosswalk_file_field_name, + wrf_hydro_channel_restart_upstream_flow_field_name, + wrf_hydro_channel_restart_downstream_flow_field_name, + wrf_hydro_channel_restart_depth_flow_field_name, + ) + else: + # TODO: Consider adding option to read cold state from route-link file + channel_initial_us_flow_const = 0.0 + channel_initial_ds_flow_const = 0.0 + channel_initial_depth_const = 0.0 + # Set initial states from cold-state + channel_initial_states_df = pd.DataFrame( + 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" + ) + channel_initial_states_df["qu0"] = channel_initial_us_flow_const + channel_initial_states_df["qd0"] = channel_initial_ds_flow_const + channel_initial_states_df["h0"] = channel_initial_depth_const + channel_initial_states_df["index"] = range(len(channel_initial_states_df)) + + if verbose: + print("channel initial states complete") + if showtiming: + print("... in %s seconds." % (time.time() - start_time)) + start_time = time.time() # STEP 5: Read (or set) QLateral Inputs if showtiming: @@ -460,7 +429,7 @@ def compute_reach_upstream_flows( jobs = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): r = list(chain.from_iterable(reach_list)) - param_df_sub = param_df.loc[ + channel_initial_states_df = param_df.loc[ r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() qlat_sub = qlats.loc[r].sort_index() @@ -470,9 +439,9 @@ def compute_reach_upstream_flows( nts, reach_list, independent_networks[tw], - param_df_sub.index.values, - param_df_sub.columns.values, - param_df_sub.values, + channel_initial_states_df.index.values, + channel_initial_states_df.columns.values, + channel_initial_states_df.values, qlat_sub.values, q0_sub.values, assume_short_ts, @@ -484,7 +453,7 @@ def compute_reach_upstream_flows( results = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): r = list(chain.from_iterable(reach_list)) - param_df_sub = param_df.loc[ + channel_initial_states_df = param_df.loc[ r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() qlat_sub = qlats.loc[r].sort_index() @@ -494,9 +463,9 @@ def compute_reach_upstream_flows( nts, reach_list, independent_networks[tw], - param_df_sub.index.values, - param_df_sub.columns.values, - param_df_sub.values, + channel_initial_states_df.index.values, + channel_initial_states_df.columns.values, + channel_initial_states_df.values, qlat_sub.values, q0_sub.values, assume_short_ts, From 0d3d2cede9f7fc44ea6556e1dd4e4ee1213f82e3 Mon Sep 17 00:00:00 2001 From: awlostowski-noaa Date: Fri, 11 Dec 2020 11:17:21 -0500 Subject: [PATCH 15/54] modify initial state variables --- .../compute_nhd_routing_SingleSeg_v02.py | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index c98826990..0a971cdc7 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -303,11 +303,11 @@ def main(): param_df, cols["waterbody"], network_data["waterbody_null_code"] ) - # initial conditions, assume to be zero - # TODO: Allow optional reading of initial conditions from WRF - q0 = pd.DataFrame( - 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" - ) +# # initial conditions, assume to be zero +# # TODO: Allow optional reading of initial conditions from WRF +# q0 = pd.DataFrame( +# 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" +# ) if verbose: print("supernetwork connections set complete") @@ -349,7 +349,7 @@ def main(): if wrf_hydro_channel_restart_file: - channel_initial_states_df = nhd_io.get_stream_restart_from_wrf_hydro( + q0 = nhd_io.get_stream_restart_from_wrf_hydro( wrf_hydro_channel_restart_file, wrf_hydro_channel_ID_crosswalk_file, wrf_hydro_channel_ID_crosswalk_file_field_name, @@ -358,18 +358,10 @@ def main(): wrf_hydro_channel_restart_depth_flow_field_name, ) else: - # TODO: Consider adding option to read cold state from route-link file - channel_initial_us_flow_const = 0.0 - channel_initial_ds_flow_const = 0.0 - channel_initial_depth_const = 0.0 - # Set initial states from cold-state - channel_initial_states_df = pd.DataFrame( + # Set cold initial state + q0 = pd.DataFrame( 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" ) - channel_initial_states_df["qu0"] = channel_initial_us_flow_const - channel_initial_states_df["qd0"] = channel_initial_ds_flow_const - channel_initial_states_df["h0"] = channel_initial_depth_const - channel_initial_states_df["index"] = range(len(channel_initial_states_df)) if verbose: print("channel initial states complete") @@ -429,7 +421,7 @@ def main(): jobs = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): r = list(chain.from_iterable(reach_list)) - channel_initial_states_df = param_df.loc[ + param_df_sub = param_df.loc[ r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() qlat_sub = qlats.loc[r].sort_index() @@ -439,9 +431,9 @@ def main(): nts, reach_list, independent_networks[tw], - channel_initial_states_df.index.values, - channel_initial_states_df.columns.values, - channel_initial_states_df.values, + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, qlat_sub.values, q0_sub.values, assume_short_ts, @@ -453,7 +445,7 @@ def main(): results = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): r = list(chain.from_iterable(reach_list)) - channel_initial_states_df = param_df.loc[ + param_df_sub = param_df.loc[ r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() qlat_sub = qlats.loc[r].sort_index() @@ -463,9 +455,9 @@ def main(): nts, reach_list, independent_networks[tw], - channel_initial_states_df.index.values, - channel_initial_states_df.columns.values, - channel_initial_states_df.values, + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, qlat_sub.values, q0_sub.values, assume_short_ts, From 947e456c963755a4e6cc457ca7a4327a6dd77fd6 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 12:30:52 -0600 Subject: [PATCH 16/54] add test strings --- src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 0a971cdc7..a00182c8c 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # coding: utf-8 # example usage: python compute_nhd_routing_SingleSeg.py -v -t -w -n Mainstems_CONUS +# python compute_nhd_routing_SingleSeg_v02.py --test -t -v --debuglevel 1 +# python compute_nhd_routing_SingleSeg_v02.py --test-full-pocono -t -v --debuglevel 1 # -*- coding: utf-8 -*- From a29f6a5b4f9e7ad835d7ccb6257e503e1bafaacd Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 12:44:28 -0600 Subject: [PATCH 17/54] refine qlateral timestep handling --- .../compute_nhd_routing_SingleSeg_v02.py | 37 +++- .../fast_reach/mc_reach.pyx | 164 ++++++++++-------- 2 files changed, 118 insertions(+), 83 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index a00182c8c..c424f9f7c 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -46,9 +46,31 @@ def _handle_args(): dest="verbose", action="store_true", ) + parser.add_argument( + "--qlat-dt", + "--qlateral-time-step", + help="Set the default qlateral timestep length", + dest="qdt", + default=3600, + ) + parser.add_argument( + "--qN", + "--qts-subdivisions", + help="number of simulation timesteps per qlateral timestep", + dest="qts_subdivisions", + type=int, + default=1, + ) + parser.add_argument( + "--dt", + "--simulation-time-step", + help="Set the default simulation timestep length", + dest="dt", + default=300, + ) parser.add_argument( "--nts", - "--number-of-qlateral-timesteps", + "--number-of-simulation-timesteps", help="Set the number of timesteps to execute. If used with ql_file or ql_folder, nts must be less than len(ql) x qN.", dest="nts", default=144, @@ -337,12 +359,12 @@ def main(): if showtiming: start_time = time.time() - param_df["dt"] = 300.0 + param_df["dt"] = dt param_df = param_df.rename(columns=nnu.reverse_dict(cols)) param_df = param_df.astype("float32") # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] - + # STEP 4: Handle Channel Initial States if showtiming: start_time = time.time() @@ -388,9 +410,10 @@ def main(): qlat_df = qlat_df[qlat_df.index.isin(connections.keys())] df_length = len(qlat_df.columns) - for x in range(df_length, 144): - qlat_df[str(x)] = 0 - qlat_df = qlat_df.astype("float32") + # TODO: These three lines seem extraneous + # for x in range(df_length, 144): + # qlat_df[str(x)] = 0 + # qlat_df = qlat_df.astype("float32") elif qlat_input_file: qlat_df = nhd_io.get_ql_from_csv(qlat_input_file) @@ -431,6 +454,7 @@ def main(): jobs.append( delayed(compute_func)( nts, + qts_subdivisions, reach_list, independent_networks[tw], param_df_sub.index.values, @@ -455,6 +479,7 @@ def main(): results.append( compute_func( nts, + qts_subdivisions, reach_list, independent_networks[tw], param_df_sub.index.values, diff --git a/src/python_routing_v02/fast_reach/mc_reach.pyx b/src/python_routing_v02/fast_reach/mc_reach.pyx index a6eb32d8c..d1a7cdef4 100644 --- a/src/python_routing_v02/fast_reach/mc_reach.pyx +++ b/src/python_routing_v02/fast_reach/mc_reach.pyx @@ -107,13 +107,13 @@ cdef void compute_reach_kernel(float qup, float quc, int nreach, const float[:,: output_buf[i, 0] = out.qdc output_buf[i, 1] = out.velc output_buf[i, 2] = out.depthc - + qup = qdp - + if assume_short_ts: quc = qup else: - quc = out.qdc + quc = out.qdc cdef void fill_buffer_column(const Py_ssize_t[:] srows, const Py_ssize_t scol, @@ -140,11 +140,21 @@ cpdef object column_mapper(object src_cols): return rv -cpdef object compute_network(int nsteps, list reaches, dict connections, - const long[:] data_idx, object[:] data_cols, const float[:,:] data_values, - const float[:, :] qlat_values, const float[:,:] initial_conditions, - # const float[:] wbody_idx, object[:] wbody_cols, const float[:, :] wbody_vals, - bint assume_short_ts=False): +cpdef object compute_network( + int nsteps, + int qts_subdivisions, + list reaches, + dict connections, + const long[:] data_idx, + object[:] data_cols, + const float[:,:] data_values, + const float[:,:] qlat_values, + const float[:,:] initial_conditions, + # const float[:] wbody_idx, + # object[:] wbody_cols, + # const float[:, :] wbody_vals, + bint assume_short_ts=False, + ): """ Compute network Args: @@ -175,7 +185,7 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, cdef: Py_ssize_t[:] srows # Source rows indexes Py_ssize_t[:] drows_tmp - + # Buffers and buffer views # These are C-contiguous. cdef float[:, ::1] buf, buf_view @@ -183,7 +193,7 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, # Source columns cdef Py_ssize_t[:] scols = np.array(column_mapper(data_cols), dtype=np.intp) - + # hard-coded column. Find a better way to do this cdef int buf_cols = 13 @@ -261,31 +271,31 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, ireach_cache += 1 iusreach_cache += 1 - + qup = 0.0 quc = 0.0 for i in range(usreachlen): - + ''' New logic was added to handle initial conditions: When timestep == 0, the flow from the upstream segments in the previous timestep - are equal to the initial conditions. + are equal to the initial conditions. ''' - - # upstream flow in the current timestep is equal the sum of flows + + # upstream flow in the current timestep is equal the sum of flows # in upstream segments, current timestep # Headwater reaches are computed before higher order reaches, so quc can # be evaulated even when the timestep == 0. quc += flowveldepth[usreach_cache[iusreach_cache + i], ts_offset] - - # upstream flow in the previous timestep is equal to the sum of flows + + # upstream flow in the previous timestep is equal to the sum of flows # in upstream segments, previous timestep if timestep > 0: qup += flowveldepth[usreach_cache[iusreach_cache + i], ts_offset - 3] else: # sum of qd0 (flow out of each segment) over all upstream reaches qup += initial_conditions[usreach_cache[iusreach_cache + i],1] - + buf_view = buf[:reachlen, :] out_view = out_buf[:reachlen, :] drows = drows_tmp[:reachlen] @@ -293,20 +303,20 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, """ qlat_values may have fewer columns than data_values if qlat data are taken from WRF hydro simulations, - which are often run at a coarser timestep than routing models. In the fill_buffer_columns call below, + which are often run at a coarser timestep than routing models. In the fill_buffer_columns call below, the second argument, which defines the column in qlat_values that data should be drawn from, is specified - such that qlat values are repeated for each of the finer routing timesteps within a WRF hydro timestep. + such that qlat values are repeated for each of the finer routing timesteps within a WRF hydro timestep. """ - fill_buffer_column(srows, - int(timestep/(nsteps/qlat_values.shape[1])), # adjust timestep to WRF-hydro timestep - drows, - 0, - qlat_values, + fill_buffer_column(srows, + int(timestep/qts_subdivisions), # adjust timestep to WRF-hydro timestep + drows, + 0, + qlat_values, buf_view) - + for i in range(scols.shape[0]): fill_buffer_column(srows, scols[i], drows, i + 1, data_values, buf_view) - + # fill buffer with qdp, depthp, velp if timestep > 0: fill_buffer_column(srows, ts_offset - 3, drows, 10, flowveldepth, buf_view) @@ -315,7 +325,7 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, else: ''' Changed made to accomodate initial conditions: - when timestep == 0, qdp, and depthp are taken from the initial_conditions array, + when timestep == 0, qdp, and depthp are taken from the initial_conditions array, using srows to properly index ''' for i in range(drows.shape[0]): @@ -335,7 +345,7 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, # Update indexes to point to next reach ireach_cache += reachlen iusreach_cache += usreachlen - + timestep += 1 return np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32') @@ -343,9 +353,9 @@ cpdef object compute_network(int nsteps, list reaches, dict connections, #---------------------------------------------------------------------------------------------------------------# #---------------------------------------------------------------------------------------------------------------# #---------------------------------------------------------------------------------------------------------------# -cpdef object compute_network_multithread(int nsteps, list reaches, dict connections, - const long[:] data_idx, object[:] data_cols, const float[:,:] data_values, - const float[:, :] qlat_values, const float[:,:] initial_conditions, +cpdef object compute_network_multithread(int nsteps, list reaches, dict connections, + const long[:] data_idx, object[:] data_cols, const float[:,:] data_values, + const float[:, :] qlat_values, const float[:,:] initial_conditions, const int[:] reach_groups, const int[:] reach_group_cache_sizes, bint assume_short_ts=False): @@ -358,7 +368,7 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti data_idx (ndarray): a 1D sorted index for data_values data_values (ndarray): a 2D array of data inputs (nodes x variables) qlats (ndarray): a 2D array of qlat values (nodes x nsteps). The index must be shared with data_values - initial_conditions (ndarray): an n x 3 array of initial conditions. + initial_conditions (ndarray): an n x 3 array of initial conditions. assume_short_ts (bool): Assume short time steps (quc = qup) Notes: Array dimensions are checked as a precondition to this method. @@ -370,16 +380,16 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti raise ValueError(f"Number of columns (timesteps) in Qlat is incorrect: expected at most ({data_idx.shape[0]}), got ({qlat_values.shape[0]}). The number of columns in Qlat must be equal to or less than the number of routing timesteps") if data_values.shape[0] != data_idx.shape[0] or data_values.shape[1] != data_cols.shape[0]: raise ValueError(f"data_values shape mismatch") - + cdef float[:,::1] flowveldepth = np.zeros((data_idx.shape[0], nsteps * 3), dtype='float32') - + cdef: Py_ssize_t[:] srows # Source rows indexes Py_ssize_t[:] drows_tmp - + # hard-coded column. Find a better way to do this cdef int buf_cols = 13 - + # Buffers and buffer views # These are C-contiguous. cdef float[:, ::1] buf, buf_view @@ -387,7 +397,7 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti cdef int maxgrouplen = max(reach_group_cache_sizes) buf = np.empty((maxgrouplen, buf_cols), dtype='float32') out_buf = np.empty((maxgrouplen, 3), dtype='float32') - + # Source columns cdef Py_ssize_t[:] scols = np.array(column_mapper(data_cols), dtype=np.intp) @@ -422,14 +432,14 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti # upstream reach cache is ordered 1D view of reaches # [-len, item, item, item, -len, item, item, -len, item, item, ...] usreach_cache = np.empty(sum(usreach_sizes) + len(usreach_sizes), dtype=np.intp) - + # ireach_cache_array ireach_cache_array = np.empty(len(reach_sizes), dtype=np.intp) iusreach_cache_array = np.empty(len(reach_sizes), dtype=np.intp) ireach_cache = 0 iusreach_cache = 0 - + ireach_cache_array[0] = 0 iusreach_cache_array[0] = 0 # copy reaches into an array @@ -452,32 +462,32 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti for bidx in binary_find(data_idx, connections[reach[0]]): usreach_cache[iusreach_cache] = bidx iusreach_cache += 1 - + if ireach < max(range(len(reaches))): ireach_cache_array[ireach+1] = ireach_cache iusreach_cache_array[ireach+1] = iusreach_cache drows_tmp = np.arange(maxgrouplen, dtype=np.intp) - + cdef Py_ssize_t[:] drows cdef int timestep = 0 cdef int ts_offset cdef int maxgroupsize = max(reach_groups) cdef float[:] qu_buf = np.empty(maxgroupsize, dtype = "float32") - cdef float[:] quc_view + cdef float[:] quc_view cdef float[:] qup_view cdef float quc, qup cdef int qu_idx cdef int buf_idx cdef Py_ssize_t[:] srowsgroup_buf = np.empty(maxgrouplen, dtype = np.intp) cdef int srows_idx - + cdef: - Py_ssize_t istart + Py_ssize_t istart Py_ssize_t iend int r - + with nogil: while timestep < nsteps: ts_offset = timestep * 3 @@ -485,10 +495,10 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti istart = 0 iend = -1 for group_i in range(len(reach_group_cache_sizes)): - + # index of final reach entry in reach_cache for this group iend += reach_groups[group_i] - + # prepare group buffers buf_view = buf[:reach_group_cache_sizes[group_i],:] out_view = out_buf[:reach_group_cache_sizes[group_i],:] @@ -496,19 +506,19 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti qup_view = qu_buf[:reach_groups[group_i]] srows = srowsgroup_buf[:reach_group_cache_sizes[group_i]] drows = drows_tmp[:reach_group_cache_sizes[group_i]] - + # extract upstream flows and populate srows qu_idx = 0 srows_idx = 0 for r in range(istart,iend+1): - + ireach_cache = ireach_cache_array[r] iusreach_cache = iusreach_cache_array[r] reachlen = -reach_cache[ireach_cache] usreachlen = -usreach_cache[iusreach_cache] iusreach_cache += 1 ireach_cache += 1 - + quc = 0.0 qup = 0.0 for i in range(usreachlen): @@ -517,25 +527,25 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti qup += flowveldepth[usreach_cache[iusreach_cache + i], ts_offset - 3] else: qup += initial_conditions[usreach_cache[iusreach_cache + i],1] - + quc_view[qu_idx] = quc qup_view[qu_idx] = qup qu_idx += 1 - + # build srows for i in range(reachlen): srows[srows_idx] = reach_cache[ireach_cache + i] srows_idx += 1 - + # fill buf_view with qlat, parameter and initial conditions data - # qlats - fill_buffer_column(srows, + # qlats + fill_buffer_column(srows, int(timestep/(nsteps/qlat_values.shape[1])), - drows, - 0, - qlat_values, + drows, + 0, + qlat_values, buf_view) - + # parameters for i in range(scols.shape[0]): fill_buffer_column(srows, scols[i], drows, i + 1, data_values, buf_view) @@ -550,15 +560,15 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti buf_view[drows[i], 10] = initial_conditions[srows[i],1] buf_view[drows[i], 11] = 0.0 buf_view[drows[i], 12] = initial_conditions[srows[i],2] - + # ------ !!!!! MULTITHREAD LOOP !!!!! ------ # # compute each reach for r in prange(istart,iend+1): - + # reach length for reach r of group ireach_cache = ireach_cache_array[r] reachlen = -reach_cache[ireach_cache] - + # total reach length of reaches preceeding reach r in group if r > istart: prevreachlen = 0 @@ -566,33 +576,33 @@ cpdef object compute_network_multithread(int nsteps, list reaches, dict connecti prevreachlen = prevreachlen + -reach_cache[ireach_cache_array[(r-(i+1))]] else: prevreachlen = 0 - + # compute reach routing - if assume_short_ts: - compute_reach_kernel(qup_view[r-istart], + if assume_short_ts: + compute_reach_kernel(qup_view[r-istart], quc_view[r-istart], # quc = qup - reachlen, + reachlen, buf_view[prevreachlen:prevreachlen+reachlen,:], out_view[prevreachlen:prevreachlen+reachlen,:], assume_short_ts) - + else: - compute_reach_kernel(qup_view[r-istart], - quc_view[r-istart], - reachlen, + compute_reach_kernel(qup_view[r-istart], + quc_view[r-istart], + reachlen, buf_view[prevreachlen:prevreachlen+reachlen,:], out_view[prevreachlen:prevreachlen+reachlen,:], assume_short_ts) # END ------ !!!!! MULTITHREAD LOOP !!!!! ------ # - - + + # place out_view results into flowveldepth for i in range(3): fill_buffer_column(drows, i, srows, ts_offset + i, out_view, flowveldepth) - - + + istart = istart + reach_groups[group_i] - + timestep += 1 - return np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32') \ No newline at end of file + return np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32') From 0f2343268a997a1b3eb2750e8fcc6ad3a1d35984 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 12:45:43 -0600 Subject: [PATCH 18/54] add test cases --- .../compute_nhd_routing_SingleSeg_v02.py | 308 ++++++++++++++---- 1 file changed, 239 insertions(+), 69 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index c424f9f7c..49d3aa129 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -76,6 +76,20 @@ def _handle_args(): default=144, type=int, ) + parser.add_argument( + "--test", + "--run-pocono2-test-example", + help="Use the data values stored in the repository for a test of the Pocono network", + dest="run_pocono2_test", + action="store_true", + ) + parser.add_argument( + "--test-full-pocono", + "--run-pocono1-test-example", + help="Use the data values stored in the repository for a test of the Mainstems_CONUS network", + dest="run_pocono1_test", + action="store_true", + ) parser.add_argument( "--sts", "--assume-short-ts", @@ -120,15 +134,16 @@ def _handle_args(): default=None, ) parser.add_argument( - "--compute_method", + "--compute-method", help="Use the cython version of the compute_network code (enter additional flag options for other compute_network possibilities).", dest="compute_method", default="standard cython compute network", ) - parser.add_argument( + supernetwork_arg_group = parser.add_mutually_exclusive_group() + supernetwork_arg_group.add_argument( "-n", "--supernetwork", - help="Choose from among the pre-programmed supernetworks (Pocono_TEST1, Pocono_TEST2, LowerColorado_Conchos_FULL_RES, Brazos_LowerColorado_ge5, Brazos_LowerColorado_FULL_RES, Brazos_LowerColorado_Named_Streams, CONUS_ge5, Mainstems_CONUS, CONUS_Named_Streams, CONUS_FULL_RES_v20", + help="Choose from among the pre-programmed supernetworks (Pocono_TEST1, Pocono_TEST2, LowerColorado_Conchos_FULL_RES, Brazos_LowerColorado_ge5, Brazos_LowerColorado_FULL_RES, Brazos_LowerColorado_Named_Streams, CONUS_ge5, Mainstems_CONUS, CONUS_Named_Streams, CONUS_FULL_RES_v20)", choices=[ "Pocono_TEST1", "Pocono_TEST2", @@ -149,11 +164,46 @@ def _handle_args(): dest="supernetwork", default="Pocono_TEST1", ) + supernetwork_arg_group.add_argument( + "-f", + "--custom-input-file", + dest="custom_input_file", + help="OR... please enter the path of a .yaml or .json file containing a custom supernetwork information. See for example test/input/yaml/CustomInput.yaml and test/input/json/CustomInput.json.", + ) parser.add_argument( - "--wrf_hydro_channel_restart_file", + "--wrf-hydro-channel-restart-file", dest="wrf_hydro_channel_restart_file", help="provide a WRF-Hydro channel warm state file (may be the same as waterbody restart file)", ) + parser.add_argument( + "--wrf-hydro-channel-ID-crosswalk-file", + dest="wrf_hydro_channel_ID_crosswalk_file", + help="provide an xarray-readable file that defines the order of the outputs in the channel restart file. Specify the ID field with --wrf_hydro_channel_ID_crosswalk_file_field_name", + ) + parser.add_argument( + "--wrf-hydro-channel-ID-crosswalk-file-field-name", + dest="wrf_hydro_channel_ID_crosswalk_file_field_name", + help="Name of the column providing the channel segment IDs in the channel crosswalk file", + default="ID", + ) + parser.add_argument( + "--wrf-hydro-channel-restart-upstream-flow-field-name", + dest="wrf_hydro_channel_restart_upstream_flow_field_name", + help="Name of the column providing the upstream flow at the beginning of the simulation.", + default="qlink1", + ) + parser.add_argument( + "--wrf-hydro-channel-restart-downstream-flow-field-name", + dest="wrf_hydro_channel_restart_downstream_flow_field_name", + help="Name of the column providing the downstream flow at the beginning of the simulation.", + default="qlink2", + ) + parser.add_argument( + "--wrf-hydro-channel-restart-depth-flow-field-name", + dest="wrf_hydro_channel_restart_depth_flow_field_name", + help="Name of the column providing the depth of flow at the beginning of the simulation.", + default="hlink", + ) ql_arg_group = parser.add_mutually_exclusive_group() ql_arg_group.add_argument( "--qlc", @@ -236,63 +286,188 @@ def constant_qlats(index_dataset, nsteps, qlat): def main(): - args = _handle_args() - nts = args.nts - debuglevel = -1 * args.debuglevel - verbose = args.verbose - showtiming = args.showtiming - supernetwork = args.supernetwork - break_network_at_waterbodies = args.break_network_at_waterbodies - csv_output_folder = args.csv_output_folder - assume_short_ts = args.assume_short_ts - # TODO: uncomment custominput file - # custom_input_file = args.custom_input_file - test_folder = pathlib.Path(root, "test") - geo_input_folder = test_folder.joinpath("input", "geo") + custom_input_file = args.custom_input_file + + if custom_input_file: + ( + supernetwork_parameters, + waterbody_parameters, + forcing_parameters, + restart_parameters, + output_parameters, + run_parameters, + ) = nio.read_custom_input(custom_input_file) + # TODO: uncomment custominput file + # qlat_const = forcing_parameters.get("qlat_const", None) + # qlat_input_file = forcing_parameters.get("qlat_input_file", None) + # qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) + # qlat_file_pattern_filter = forcing_parameters.get( + # "qlat_file_pattern_filter", None + # ) + # qlat_file_index_col = forcing_parameters.get("qlat_file_index_col", None) + # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) + # else: + # TODO: uncomment custominput file + else: + dt = args.dt + nts = args.nts + qts_subdivisions = args.qts_subdivisions + debuglevel = -1 * args.debuglevel + verbose = args.verbose + showtiming = args.showtiming + supernetwork = args.supernetwork + break_network_at_waterbodies = args.break_network_at_waterbodies + csv_output_folder = args.csv_output_folder + assume_short_ts = args.assume_short_ts + test_folder = pathlib.Path(root, "test") + geo_input_folder = test_folder.joinpath("input", "geo") + + parallel_compute_method = args.parallel_compute_method + + cpu_pool = args.cpu_pool + compute_method = args.compute_method + wrf_hydro_channel_restart_file = args.wrf_hydro_channel_restart_file + wrf_hydro_channel_ID_crosswalk_file = args.wrf_hydro_channel_ID_crosswalk_file + wrf_hydro_channel_ID_crosswalk_file_field_name = ( + args.wrf_hydro_channel_ID_crosswalk_file_field_name + ) + wrf_hydro_channel_restart_upstream_flow_field_name = ( + args.wrf_hydro_channel_restart_upstream_flow_field_name + ) + wrf_hydro_channel_restart_downstream_flow_field_name = ( + args.wrf_hydro_channel_restart_downstream_flow_field_name + ) + wrf_hydro_channel_restart_depth_flow_field_name = ( + args.wrf_hydro_channel_restart_depth_flow_field_name + ) + qlat_const = float(args.qlat_const) + qlat_input_folder = args.qlat_input_folder + qlat_input_file = args.qlat_input_file + qlat_file_pattern_filter = args.qlat_file_pattern_filter + qlat_file_index_col = args.qlat_file_index_col + qlat_file_value_col = args.qlat_file_value_col + # print(forcing_parameters,qlat_const) + # TODO: Make these commandline args + """##NHD Subset (Brazos/Lower Colorado)""" + # supernetwork = 'Brazos_LowerColorado_Named_Streams' + # supernetwork = 'Brazos_LowerColorado_ge5' + # supernetwork = 'Pocono_TEST1' + """##NHD CONUS order 5 and greater""" + # supernetwork = 'CONUS_ge5' + """These are large -- be careful""" + # supernetwork = 'Mainstems_CONUS' + # supernetwork = 'CONUS_FULL_RES_v20' + # supernetwork = 'CONUS_Named_Streams' #create a subset of the full resolution by reading the GNIS field + # supernetwork = 'CONUS_Named_combined' #process the Named streams through the Full-Res paths to join the many hanging reaches + + run_pocono2_test = args.run_pocono2_test + run_pocono1_test = args.run_pocono1_test + + if run_pocono2_test: + if verbose: + print("running test case for Pocono_TEST2 domain") + # Overwrite the following test defaults + supernetwork = "Pocono_TEST2" + break_network_at_waterbodies = False + qts_subdivisions = 1 # change qts_subdivisions = 1 as default + dt = 300 / qts_subdivisions + nts = 144 * qts_subdivisions + csv_output = {"csv_output_folder": os.path.join(root, "test", "output", "text")} + nc_output_folder = os.path.join(root, "test", "output", "text") + # test 1. Take lateral flow from re-formatted wrf-hydro output from Pocono Basin simulation + qlat_input_file = os.path.join( + root, r"test/input/geo/PoconoSampleData2/Pocono_ql_testsamp1_nwm_mc.csv" + ) - # TODO: uncomment custominput file - # if custom_input_file: - # ( - # supernetwork_parameters, - # waterbody_parameters, - # forcing_parameters, - # restart_parameters, - # output_parameters, - # run_parameters, - # ) = nhd_io.read_custom_input(custom_input_file) - - # qlat_const = forcing_parameters.get("qlat_const", None) - # qlat_input_file = forcing_parameters.get("qlat_input_file", None) - # qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) - # qlat_file_pattern_filter = forcing_parameters.get( - # "qlat_file_pattern_filter", None - # ) - # qlat_file_index_col = forcing_parameters.get("qlat_file_index_col", None) - # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) - # else: - wrf_hydro_channel_restart_file = args.wrf_hydro_channel_restart_file - # TODO: uncomment custominput file - qlat_const = float(args.qlat_const) - qlat_input_folder = args.qlat_input_folder - qlat_input_file = args.qlat_input_file - qlat_file_pattern_filter = args.qlat_file_pattern_filter - qlat_file_index_col = args.qlat_file_index_col - qlat_file_value_col = args.qlat_file_value_col - # print(forcing_parameters,qlat_const) - # TODO: Make these commandline args - """##NHD Subset (Brazos/Lower Colorado)""" - # supernetwork = 'Brazos_LowerColorado_Named_Streams' - # supernetwork = 'Brazos_LowerColorado_ge5' - # supernetwork = 'Pocono_TEST1' - """##NHD CONUS order 5 and greater""" - # supernetwork = 'CONUS_ge5' - """These are large -- be careful""" - # supernetwork = 'Mainstems_CONUS' - # supernetwork = 'CONUS_FULL_RES_v20' - # supernetwork = 'CONUS_Named_Streams' #create a subset of the full resolution by reading the GNIS field - # supernetwork = 'CONUS_Named_combined' #process the Named streams through the Full-Res paths to join the many hanging reaches + elif run_pocono1_test: + # NOTE: The test case for the Pocono basin was derived from this + # resource on HydroShare, developed by aaraney and sourced from the + # wrf_hydro_nwm_public repository on GitHub + # see: https://www.hydroshare.org/resource/03ca354200e540018d44183598890448/ + # By downloading aaraney's docker job scheduler repo from GitHub, one can + # execute the WRF-Hydro model that generated the test results + # see: https://github.com/aaraney/NWM-Dockerized-Job-Scheduler + if verbose: + print("running test case for Pocono_TEST1 domain") + # Overwrite the following test defaults + + NWM_test_path = os.path.join( + root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/" + ) + # lakeparm_file = os.path.join( + # NWM_test_path, "primary_domain", "DOMAIN", "LAKEPARM.nc", + # ) + routelink_file = os.path.join( + NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", + ) + time_string = "2017-12-31_06-00_DOMAIN1" + wrf_hydro_restart_file = os.path.join( + NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string + ) + supernetwork_parameters = { + "title_string": "Custom Input Example (using Pocono Test Example datafile)", + "geo_file_path": routelink_file, + "cols_as_text": False, + "key_col": 16, # "link", + "downstream_col": 22, # "to", + "length_col": 3, # "Length", + "manningn_col": 18, # "n", + "manningncc_col": 19, # "nCC", + "slope_col": 8, # "So", + "bottomwidth_col": 0, # "BtmWdth", + "topwidth_col": 9, # "TopWdth", + "topwidthcc_col": 10, # "TopWdthCC", + "waterbody_col": 6, # "NHDWaterbodyComID", + "waterbody_null_code": -9999, + "MusK_col": 4, # "MusK", + "MusX_col": 5, # "MusX", + "ChSlp_col": 1, # "ChSlp", + "terminal_code": 0, + "driver_string": "NetCDF", + "layer_string": 0, + } + # waterbody_parameters = { + # "level_pool": { + # "level_pool_waterbody_parameter_file_path": lakeparm_file, + # "level_pool_waterbody_id": "lake_id", + # "level_pool_waterbody_area": "LkArea", + # "level_pool_weir_elevation": "WeirE", + # "level_pool_waterbody_max_elevation": "LkMxE", + # "level_pool_outfall_weir_coefficient": "WeirC", + # "level_pool_outfall_weir_length": "WeirL", + # "level_pool_overall_dam_length": "DamL", + # "level_pool_orifice_elevation": "OrificeE", + # "level_pool_orifice_coefficient": "OrificeC", + # "level_pool_orifice_area": "OrificeA", + # } + # } + # break_network_at_waterbodies = True + qts_subdivisions = 12 + dt = 3600 / qts_subdivisions + nts = 24 * qts_subdivisions + csv_output = None + nc_output_folder = None + # build a time string to specify input date + wrf_hydro_channel_restart_file = wrf_hydro_restart_file + wrf_hydro_channel_ID_crosswalk_file = routelink_file + wrf_hydro_channel_ID_crosswalk_file_field_name = "link" + wrf_hydro_channel_restart_upstream_flow_field_name = "qlink1" + wrf_hydro_channel_restart_downstream_flow_field_name = "qlink2" + wrf_hydro_channel_restart_depth_flow_field_name = "hlink" + # wrf_hydro_waterbody_restart_file = wrf_hydro_restart_file + # wrf_hydro_waterbody_ID_crosswalk_file = lakeparm_file + # wrf_hydro_waterbody_ID_crosswalk_file_field_name = "lake_id" + # wrf_hydro_waterbody_crosswalk_filter_file = routelink_file + # wrf_hydro_waterbody_crosswalk_filter_file_field_name = "NHDWaterbodyComID" + # wrf_hydro_waterbody_crosswalk_file_output_order_field= "AscendingIndex" + qlat_input_folder = os.path.join( + root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/" + ) + qlat_file_pattern_filter = "/*.CHRTOUT_DOMAIN1" + qlat_file_index_col = "feature_id" + qlat_file_value_col = "q_lateral" if verbose: print("creating supernetwork connections set") @@ -301,7 +476,7 @@ def main(): # STEP 1 network_data = nnu.set_supernetwork_data( - supernetwork=args.supernetwork, + supernetwork=supernetwork, geo_input_folder=geo_input_folder, verbose=False, debuglevel=debuglevel, @@ -327,11 +502,11 @@ def main(): param_df, cols["waterbody"], network_data["waterbody_null_code"] ) -# # initial conditions, assume to be zero -# # TODO: Allow optional reading of initial conditions from WRF -# q0 = pd.DataFrame( -# 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" -# ) + # # initial conditions, assume to be zero + # # TODO: Allow optional reading of initial conditions from WRF + # q0 = pd.DataFrame( + # 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" + # ) if verbose: print("supernetwork connections set complete") @@ -431,11 +606,6 @@ def main(): print("... in %s seconds." % (time.time() - start_time)) start_time = time.time() - parallel_compute_method = args.parallel_compute_method - - cpu_pool = args.cpu_pool - compute_method = args.compute_method - if compute_method == "standard cython compute network": compute_func = mc_reach.compute_network else: From e7586d0e01e92e582f306896781feb8da97b6398 Mon Sep 17 00:00:00 2001 From: John David Mattern Date: Tue, 17 Nov 2020 18:28:26 -0500 Subject: [PATCH 19/54] Cython to Fortran bindings to Reservoir module and Python unit tests of binding calls for each type of Reservoir --- .../kernels/compute_kernel_hybrid.pyx | 208 +++ .../kernels/compute_kernel_lp.pyx | 178 +++ .../kernels/compute_kernel_rfc.pyx | 202 +++ .../kernels/fortran/Reservoirs/.gitignore | 3 + .../kernels/fortran/Reservoirs/CMakeLists.txt | 14 + .../Reservoirs/Level_Pool/CMakeLists.txt | 10 + .../fortran/Reservoirs/Level_Pool/Makefile | 28 + .../Reservoirs/Level_Pool/Original_Makefile | 28 + .../Reservoirs/Level_Pool/module_levelpool.F | 416 ++++++ .../Level_Pool/module_levelpool_properties.F | 72 + .../Level_Pool/module_levelpool_state.F | 45 + .../Level_Pool/module_levelpool_tests.F | 256 ++++ .../CMakeLists.txt | 12 + .../Persistence_Level_Pool_Hybrid/Makefile | 28 + .../module_persistence_levelpool_hybrid.F | 450 +++++++ ..._persistence_levelpool_hybrid_properties.F | 139 ++ ...odule_persistence_levelpool_hybrid_state.F | 108 ++ ...odule_persistence_levelpool_hybrid_tests.F | 574 ++++++++ .../Reservoirs/RFC_Forecasts/CMakeLists.txt | 10 + .../fortran/Reservoirs/RFC_Forecasts/Makefile | 28 + .../RFC_Forecasts/module_rfc_forecasts.F | 387 ++++++ .../module_rfc_forecasts_properties.F | 96 ++ .../module_rfc_forecasts_state.F | 88 ++ .../module_rfc_forecasts_tests.F | 435 ++++++ .../fortran/Reservoirs/bind_hybrid.f90 | 154 +++ .../kernels/fortran/Reservoirs/bind_lp.f90 | 89 ++ .../kernels/fortran/Reservoirs/bind_rfc.f90 | 140 ++ .../kernels/fortran/Reservoirs/makefile | 53 + .../fortran/Reservoirs/module_hydro_stop.F | 48 + .../fortran/Reservoirs/module_reservoir.F | 150 +++ ...dule_reservoir_read_rfc_time_series_data.F | 237 ++++ .../module_reservoir_read_timeslice_data.F | 357 +++++ .../Reservoirs/module_reservoir_utilities.F | 1196 +++++++++++++++++ .../kernels/fortran/Reservoirs/test_binding.c | 35 + .../Reservoir_Binding/kernels/readme | 10 + ...10-10-01_06:00:00.15min.usgsTimeSlice.ncdf | Bin 0 -> 1156 bytes ...19-08-18_00.60min.CCHC1.RFCTimeSeries.ncdf | Bin 0 -> 47863 bytes .../reservoir_index_short_range.nc | Bin 0 -> 114312 bytes .../Reservoir_Binding/kernels/setup.py | 38 + .../kernels/test_compute_kernel.py | 239 ++++ 40 files changed, 6561 insertions(+) create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_hybrid.pyx create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_rfc.pyx create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/.gitignore create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/CMakeLists.txt create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/CMakeLists.txt create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Makefile create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_properties.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_state.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_tests.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/CMakeLists.txt create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/Makefile create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_properties.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_state.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_tests.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/CMakeLists.txt create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/Makefile create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_properties.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_state.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_tests.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_hydro_stop.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_rfc_time_series_data.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_timeslice_data.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_utilities.F create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/readme create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2010-10-01_06:00:00.15min.usgsTimeSlice.ncdf create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2019-08-18_00.60min.CCHC1.RFCTimeSeries.ncdf create mode 100755 src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/reservoir_index_short_range.nc create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/setup.py create mode 100644 src/fortran_routing/Reservoir_Binding/kernels/test_compute_kernel.py diff --git a/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_hybrid.pyx b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_hybrid.pyx new file mode 100644 index 000000000..5300cf5cc --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_hybrid.pyx @@ -0,0 +1,208 @@ +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.stdio cimport printf +""" +Externally defined symbols +""" + +############ Persistence Level Pool Hybrid Reservoir Interface ############ +cdef extern void* get_hybrid_handle() nogil; + +cdef extern void init_hybrid(void* handle, float *water_elevation, float *lake_area, float *weir_elevation, + float *weir_coefficient, float *weir_length, float *dam_length, float *orifice_elevation, + float *orifice_coefficient, float *orifice_area, float *max_depth, float *initial_fractional_depth, int *lake_number, int *reservoir_type, char *reservoir_parameter_file, char *start_date, char *usgs_timeslice_path, char *usace_timeslice_path, int *observation_lookback_hours, int *observation_update_time_interval_seconds) nogil; + + +cdef extern void run_hybrid(void* handle, float *inflow, float *lateral_inflow, + float *water_elevation, float *outflow, float *routing_period) nogil; + +cdef extern void free_hybrid(void* handle); + + +############ Other Reservoir Interface ############ + +#ctypedef enum compute_type: REACH, RESERVOIR_HYBRID +ctypedef enum compute_type: RESERVOIR_HYBRID + +cdef class compute_kernel_hybrid: + """ + An encapsulation of a compute kernel that thread will execute + """ + + cdef int num_inputs; + cdef int num_outputs; + #cdef double* inputs; + #cdef double* outputs; + cdef float* inputs; + cdef float* outputs; + + + def __init__(self, num_inputs, num_outputs): + """ + Allocate the input and output arrays + """ + #self.inputs = PyMem_Malloc(num_inputs * sizeof(double)) + #self.outputs = PyMem_Malloc(num_outputs * sizeof(double)) + self.inputs = PyMem_Malloc(num_inputs * sizeof(float)) + self.outputs = PyMem_Malloc(num_outputs * sizeof(float)) + + + + def __dealloc__(self): + PyMem_Free(self.inputs) + PyMem_Free(self.outputs) + +#David Mattern commenting out for now +# #Now the fun part, what do we ACTUALLY compute??? +# cdef void compute(self): +# pass + + +''' +cdef class mc_kernel(compute_kernel): + #TODO document these attributes + cdef float dt, dx, bw, tw, twcc, n, ncc, cs, s0, qdp, velp, depthp + #Hold the previous accumulated upstream flow + cdef float qup + cdef bint assume_short_ts + + def __init__(dt, dx, tw, twcc, n, ncc, cs, s0, qdp, velp, dethp, qup=0, assume_short_ts=False): + """ + construct the kernel based on passed parameters + """ + self.dt = dt + self.dx = dx + self.tw = tw + self.twcc = twcc + self.n = n + self.ncc = ncc + self.cs = cs + self.s0 = s0 + self.qdp = qdp + self.velp = velp + self.depthp = depthp + #Allow optional previous upstream (initial condition) + self.qup = qup + self.assume_short_ts = assume_short_ts + + #FIXME return more than one value? + cpdef float run(float quc, float qlat) nogil: + """ + run the muskingcung calculation + """ + cdef reach.QVD rv + cdef reach.QVD *out = &rv + + reach.muskingcunge( + self.dt, + self.qup, + quc, + self.qdp, + qlat, + self.dx, + self.bw, + self.tw, + self.twcc, + n, + ncc, + cs, + s0, + velp, + depthp, + out) + #Record quc as qup for next call + self.qup = out.qdc + cdef q_out = out.qdc + if self.assume_short_ts: + #Short timestep assumption means current and previous are the same + q_out = self.qup + #FIXME return other things??? + return q_out +''' +cdef class hybrid_kernel(compute_kernel_hybrid): + """ + Subclass for computing Hybrid reservoir + """ + #Non python accessible attribute + cdef void* hybrid_handle; + + #Reservoir attributes + cdef float water_elevation + cdef float lake_area + cdef float weir_elevation + cdef float weir_coefficient + cdef float weir_length + cdef float dam_length + cdef float orifice_elevation + cdef float orifice_coefficient + cdef float orifice_area + cdef float max_depth + cdef float initial_fractional_depth + cdef int lake_number + cdef int reservoir_type + + cdef bytes reservoir_parameter_file + cdef bytes start_date + cdef bytes usgs_timeslice_path + cdef bytes usace_timeslice_path + + cdef int observation_lookback_hours + cdef int observation_update_time_interval_seconds + + + def __init__(self, num_inputs, num_outputs, + water_elevation, lake_area, weir_elevation, weir_coefficient, weir_length, + dam_length, orifice_elevation, orifice_coefficient, orifice_area, + max_depth, initial_fractional_depth, lake_number, reservoir_type, reservoir_parameter_file, + start_date, usgs_timeslice_path, usace_timeslice_path, observation_lookback_hours, observation_update_time_interval_seconds): + super().__init__(num_inputs, num_outputs) + self.water_elevation = water_elevation + self.lake_area = lake_area + self.weir_elevation = weir_elevation + self.weir_coefficient = weir_coefficient + self.weir_length = weir_length + self.dam_length = dam_length + self.orifice_elevation = orifice_elevation + self.orifice_coefficient = orifice_coefficient + self.orifice_area = orifice_area + self.max_depth = max_depth + self.initial_fractional_depth = initial_fractional_depth + self.lake_number = lake_number + self.reservoir_type = reservoir_type + self.reservoir_parameter_file = reservoir_parameter_file + self.start_date = start_date + self.usgs_timeslice_path = usgs_timeslice_path + self.usace_timeslice_path = usace_timeslice_path + self.observation_lookback_hours = observation_lookback_hours + self.observation_update_time_interval_seconds = observation_update_time_interval_seconds + + cdef char* reservoir_parameter_file_ptr = reservoir_parameter_file + cdef char* start_date_ptr = start_date + cdef char* usgs_timeslice_path_ptr = usgs_timeslice_path + cdef char* usace_timeslice_path_ptr = usace_timeslice_path + + with nogil: + self.hybrid_handle = get_hybrid_handle() + init_hybrid(self.hybrid_handle, &self.water_elevation, &self.lake_area, + &self.weir_elevation, &self.weir_coefficient, &self.weir_length, + &self.dam_length, &self.orifice_elevation, &self.orifice_coefficient, + &self.orifice_area, &self.max_depth, &self.initial_fractional_depth, + &self.lake_number, &self.reservoir_type, reservoir_parameter_file_ptr, + start_date_ptr, usgs_timeslice_path_ptr, usace_timeslice_path_ptr, + &self.observation_lookback_hours, &self.observation_update_time_interval_seconds) + + + def __dealloc__(self): + free_hybrid(self.hybrid_handle) + + cpdef float run(self, float inflow, float lateral_inflow, float routing_period): + cdef float outflow = 0.0 + + with nogil: + run_hybrid(self.hybrid_handle, &inflow, &lateral_inflow, &self.water_elevation, &outflow, &routing_period) + #printf("outflow: %f\n", outflow) + return outflow + +#David Mattern commenting out for now +# cdef void compute2(self) nogil: +# #run_hybrid(self.hybrid_handle) +# pass diff --git a/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx new file mode 100644 index 000000000..2aeb39856 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx @@ -0,0 +1,178 @@ +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.stdio cimport printf +""" +Externally defined symbols +""" +############ Level Pool Reservoir Interface ############ +cdef extern void* get_lp_handle() nogil; + +cdef extern void init_lp(void* handle, float *water_elevation, float *lake_area, float *weir_elevation, + float *weir_coefficient, float *weir_length, float *dam_length, float *orifice_elevation, + float *orifice_coefficient, float *orifice_area, float *max_depth, int *lake_number) nogil; + +cdef extern void run_lp(void* handle, float *inflow, float *lateral_inflow, + float *water_elevation, float *outflow, float *routing_period) nogil; + +cdef extern void free_lp(void* handle); + + + +############ Other Reservoir Interface ############ + +#ctypedef enum compute_type: REACH, RESERVOIR_LP +ctypedef enum compute_type: RESERVOIR_LP + + +cdef class compute_kernel_lp: + """ + An encapsulation of a compute kernel that thread will execute + """ + + cdef int num_inputs; + cdef int num_outputs; + #cdef double* inputs; + #cdef double* outputs; + cdef float* inputs; + cdef float* outputs; + + + def __init__(self, num_inputs, num_outputs): + """ + Allocate the input and output arrays + """ + #self.inputs = PyMem_Malloc(num_inputs * sizeof(double)) + #self.outputs = PyMem_Malloc(num_outputs * sizeof(double)) + self.inputs = PyMem_Malloc(num_inputs * sizeof(float)) + self.outputs = PyMem_Malloc(num_outputs * sizeof(float)) + + + + def __dealloc__(self): + PyMem_Free(self.inputs) + PyMem_Free(self.outputs) + + +#David Mattern commenting out for now +# #Now the fun part, what do we ACTUALLY compute??? +# cdef void compute(self): +# pass +''' +cdef class mc_kernel(compute_kernel): + #TODO document these attributes + cdef float dt, dx, bw, tw, twcc, n, ncc, cs, s0, qdp, velp, depthp + #Hold the previous accumulated upstream flow + cdef float qup + cdef bint assume_short_ts + + def __init__(dt, dx, tw, twcc, n, ncc, cs, s0, qdp, velp, dethp, qup=0, assume_short_ts=False): + """ + construct the kernel based on passed parameters + """ + self.dt = dt + self.dx = dx + self.tw = tw + self.twcc = twcc + self.n = n + self.ncc = ncc + self.cs = cs + self.s0 = s0 + self.qdp = qdp + self.velp = velp + self.depthp = depthp + #Allow optional previous upstream (initial condition) + self.qup = qup + self.assume_short_ts = assume_short_ts + + #FIXME return more than one value? + cpdef float run(float quc, float qlat) nogil: + """ + run the muskingcung calculation + """ + cdef reach.QVD rv + cdef reach.QVD *out = &rv + + reach.muskingcunge( + self.dt, + self.qup, + quc, + self.qdp, + qlat, + self.dx, + self.bw, + self.tw, + self.twcc, + n, + ncc, + cs, + s0, + velp, + depthp, + out) + #Record quc as qup for next call + self.qup = out.qdc + cdef q_out = out.qdc + if self.assume_short_ts: + #Short timestep assumption means current and previous are the same + q_out = self.qup + #FIXME return other things??? + return q_out +''' +cdef class lp_kernel(compute_kernel_lp): + """ + Subclass for computing LevelPool reservoir + """ + #Non python accessible attribute + cdef void* lp_handle; + + #Reservoir attributes + cdef float water_elevation + cdef float lake_area + cdef float weir_elevation + cdef float weir_coefficient + cdef float weir_length + cdef float dam_length + cdef float orifice_elevation + cdef float orifice_coefficient + cdef float orifice_area + cdef float max_depth + cdef int lake_number + + def __init__(self, num_inputs, num_outputs, + water_elevation, lake_area, weir_elevation, weir_coefficient, weir_length, + dam_length, orifice_elevation, orifice_coefficient, orifice_area, + max_depth, lake_number): + super().__init__(num_inputs, num_outputs) + self.water_elevation = water_elevation + self.lake_area = lake_area + self.weir_elevation = weir_elevation + self.weir_coefficient = weir_coefficient + self.weir_length = weir_length + self.dam_length = dam_length + self.orifice_elevation = orifice_elevation + self.orifice_coefficient = orifice_coefficient + self.orifice_area = orifice_area + self.max_depth = max_depth + self.lake_number = lake_number + + with nogil: + self.lp_handle = get_lp_handle() + init_lp(self.lp_handle, &self.water_elevation, &self.lake_area, + &self.weir_elevation, &self.weir_coefficient, &self.weir_length, + &self.dam_length, &self.orifice_elevation, &self.orifice_coefficient, + &self.orifice_area, &self.max_depth, &self.lake_number) + + def __dealloc__(self): + free_lp(self.lp_handle) + + cpdef float run(self, float inflow, float lateral_inflow, float routing_period): + cdef float outflow = 0.0 + + with nogil: + run_lp(self.lp_handle, &inflow, &lateral_inflow, &self.water_elevation, &outflow, &routing_period) + #printf("outflow: %f\n", outflow) + return outflow + +#David Mattern commenting out for now +# cdef void compute2(self) nogil: +# #run_lp(self.lp_handle) +# pass diff --git a/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_rfc.pyx b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_rfc.pyx new file mode 100644 index 000000000..1dbbc2654 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_rfc.pyx @@ -0,0 +1,202 @@ +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.stdio cimport printf +""" +Externally defined symbols +""" + +############ RFC Forecasts Reservoir Interface ############ +cdef extern void* get_rfc_handle() nogil; + +cdef extern void init_rfc(void* handle, float *water_elevation, float *lake_area, float *weir_elevation, + float *weir_coefficient, float *weir_length, float *dam_length, float *orifice_elevation, + float *orifice_coefficient, float *orifice_area, float *max_depth, float *initial_fractional_depth, int *lake_number, int *reservoir_type, char *reservoir_parameter_file, char *start_date, char *time_series_path, int *forecast_lookback_hours) nogil; + + +cdef extern void run_rfc(void* handle, float *inflow, float *lateral_inflow, + float *water_elevation, float *outflow, float *routing_period) nogil; + +cdef extern void free_rfc(void* handle); + + +############ Other Reservoir Interface ############ + +#ctypedef enum compute_type: REACH, RESERVOIR_RFC +ctypedef enum compute_type: RESERVOIR_RFC + +cdef class compute_kernel_rfc: + """ + An encapsulation of a compute kernel that thread will execute + """ + + cdef int num_inputs; + cdef int num_outputs; + #cdef double* inputs; + #cdef double* outputs; + cdef float* inputs; + cdef float* outputs; + + + def __init__(self, num_inputs, num_outputs): + """ + Allocate the input and output arrays + """ + #self.inputs = PyMem_Malloc(num_inputs * sizeof(double)) + #self.outputs = PyMem_Malloc(num_outputs * sizeof(double)) + self.inputs = PyMem_Malloc(num_inputs * sizeof(float)) + self.outputs = PyMem_Malloc(num_outputs * sizeof(float)) + + + + def __dealloc__(self): + PyMem_Free(self.inputs) + PyMem_Free(self.outputs) + +#David Mattern commenting out for now +# #Now the fun part, what do we ACTUALLY compute??? +# cdef void compute(self): +# pass + + +''' +cdef class mc_kernel(compute_kernel): + #TODO document these attributes + cdef float dt, dx, bw, tw, twcc, n, ncc, cs, s0, qdp, velp, depthp + #Hold the previous accumulated upstream flow + cdef float qup + cdef bint assume_short_ts + + def __init__(dt, dx, tw, twcc, n, ncc, cs, s0, qdp, velp, dethp, qup=0, assume_short_ts=False): + """ + construct the kernel based on passed parameters + """ + self.dt = dt + self.dx = dx + self.tw = tw + self.twcc = twcc + self.n = n + self.ncc = ncc + self.cs = cs + self.s0 = s0 + self.qdp = qdp + self.velp = velp + self.depthp = depthp + #Allow optional previous upstream (initial condition) + self.qup = qup + self.assume_short_ts = assume_short_ts + + #FIXME return more than one value? + cpdef float run(float quc, float qlat) nogil: + """ + run the muskingcung calculation + """ + cdef reach.QVD rv + cdef reach.QVD *out = &rv + + reach.muskingcunge( + self.dt, + self.qup, + quc, + self.qdp, + qlat, + self.dx, + self.bw, + self.tw, + self.twcc, + n, + ncc, + cs, + s0, + velp, + depthp, + out) + #Record quc as qup for next call + self.qup = out.qdc + cdef q_out = out.qdc + if self.assume_short_ts: + #Short timestep assumption means current and previous are the same + q_out = self.qup + #FIXME return other things??? + return q_out +''' +cdef class rfc_kernel(compute_kernel_rfc): + """ + Subclass for computing RFC reservoir + """ + #Non python accessible attribute + cdef void* rfc_handle; + + #Reservoir attributes + cdef float water_elevation + cdef float lake_area + cdef float weir_elevation + cdef float weir_coefficient + cdef float weir_length + cdef float dam_length + cdef float orifice_elevation + cdef float orifice_coefficient + cdef float orifice_area + cdef float max_depth + cdef float initial_fractional_depth + cdef int lake_number + cdef int reservoir_type + + cdef bytes reservoir_parameter_file + cdef bytes start_date + cdef bytes time_series_path + + cdef int forecast_lookback_hours + + + def __init__(self, num_inputs, num_outputs, + water_elevation, lake_area, weir_elevation, weir_coefficient, weir_length, + dam_length, orifice_elevation, orifice_coefficient, orifice_area, + max_depth, initial_fractional_depth, lake_number, reservoir_type, reservoir_parameter_file, + start_date, time_series_path, forecast_lookback_hours): + super().__init__(num_inputs, num_outputs) + self.water_elevation = water_elevation + self.lake_area = lake_area + self.weir_elevation = weir_elevation + self.weir_coefficient = weir_coefficient + self.weir_length = weir_length + self.dam_length = dam_length + self.orifice_elevation = orifice_elevation + self.orifice_coefficient = orifice_coefficient + self.orifice_area = orifice_area + self.max_depth = max_depth + self.initial_fractional_depth = initial_fractional_depth + self.lake_number = lake_number + self.reservoir_type = reservoir_type + self.reservoir_parameter_file = reservoir_parameter_file + self.start_date = start_date + self.time_series_path = time_series_path + self.forecast_lookback_hours = forecast_lookback_hours + + cdef char* reservoir_parameter_file_ptr = reservoir_parameter_file + cdef char* start_date_ptr = start_date + cdef char* time_series_path_ptr = time_series_path + + with nogil: + self.rfc_handle = get_rfc_handle() + init_rfc(self.rfc_handle, &self.water_elevation, &self.lake_area, + &self.weir_elevation, &self.weir_coefficient, &self.weir_length, + &self.dam_length, &self.orifice_elevation, &self.orifice_coefficient, + &self.orifice_area, &self.max_depth, &self.initial_fractional_depth, + &self.lake_number, &self.reservoir_type, reservoir_parameter_file_ptr, + start_date_ptr, time_series_path_ptr, &self.forecast_lookback_hours) + + + def __dealloc__(self): + free_rfc(self.rfc_handle) + + cpdef float run(self, float inflow, float lateral_inflow, float routing_period): + cdef float outflow = 0.0 + + with nogil: + run_rfc(self.rfc_handle, &inflow, &lateral_inflow, &self.water_elevation, &outflow, &routing_period) + #printf("outflow: %f\n", outflow) + return outflow + +#David Mattern commenting out for now +# cdef void compute2(self) nogil: +# #run_rfc(self.rfc_handle) +# pass diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/.gitignore b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/.gitignore new file mode 100644 index 000000000..8a1212e22 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/.gitignore @@ -0,0 +1,3 @@ +resevoir_tests +test_main +*.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/CMakeLists.txt b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/CMakeLists.txt new file mode 100644 index 000000000..c434b276d --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required (VERSION 2.8) + +add_library(hydro_routing_reservoirs STATIC + module_reservoir.F + module_reservoir_read_timeslice_data.F + module_reservoir_read_rfc_time_series_data.F + module_reservoir_utilities.F +) + +add_subdirectory("Level_Pool") +add_subdirectory("Persistence_Level_Pool_Hybrid") +add_subdirectory("RFC_Forecasts") + +add_dependencies(hydro_routing_reservoirs hydro_utils) diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/CMakeLists.txt b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/CMakeLists.txt new file mode 100644 index 000000000..46766ce13 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 2.8) + +add_library(hydro_routing_reservoirs_levelpool STATIC + module_levelpool.F + module_levelpool_state.F + module_levelpool_properties.F +) + +add_dependencies(hydro_routing_reservoirs_levelpool hydro_routing_reservoirs) + diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Makefile new file mode 100644 index 000000000..728752b4c --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Makefile @@ -0,0 +1,28 @@ + +include ../macros + +MODFLAG := -I ../mod + +%.o : %.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) $< + +.PHONY: all mod test + +all: mod + +mod: + #Build each sub module then build the module that depends on all sub modules + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_properties.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_state.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_tests.F + ar -r ../lib/libHYDRO.a module_levelpool_properties.o + ar -r ../lib/libHYDRO.a module_levelpool_state.o + ar -r ../lib/libHYDRO.a module_levelpool.o + ar -r ../lib/libHYDRO.a module_levelpool_tests.o + + cp *.mod ../mod + +clean: + rm -f *.o + rm -f *.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile new file mode 100644 index 000000000..03e1ef895 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile @@ -0,0 +1,28 @@ + +include ../macros + +MODFLAG := -I ../MPP -I ../mod + +%.o : %.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) $< + +.PHONY: all mod test + +all: mod + +mod: + #Build each sub module then build the module that depends on all sub modules + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_properties.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_state.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_tests.F + ar -r ../lib/libHYDRO.a module_levelpool_properties.o + ar -r ../lib/libHYDRO.a module_levelpool_state.o + ar -r ../lib/libHYDRO.a module_levelpool.o + ar -r ../lib/libHYDRO.a module_levelpool_tests.o + + cp *.mod ../mod + +clean: + rm -f *.o + rm -f *.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F new file mode 100644 index 000000000..3c903a205 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F @@ -0,0 +1,416 @@ +! This module defines and instantiates objects +! for a level pool type reservoir. The level +! pool reservoir type inherits input and +! output types from the reservoir base +! module and calls instantiation of these into +! sub-objects. The level pool reservoir type +! also points to types for level pool properties +! and state and calls instantiation of these into +! sub-objects. This module also contains the +! subroutine to run level pool reservoir that is +! derived from the reservoir base type interface +! to run reservoir. Running level pool will +! then call the LEVELPOOL_PHYSICS subroutine, which +! processes the given inputs, properties, and +! state for a particular level pool reservoir and +! returns the output/outflow. + +module module_levelpool + + use module_levelpool_properties, only: levelpool_properties_interface + use module_levelpool_state, only: levelpool_state_interface + use module_reservoir, only: reservoir, reservoir_input, & + reservoir_output + !use module_hydro_stop, only: HYDRO_stop + +!#ifdef RESERVOIR_D +! use module_reservoir_utilities, only: create_levelpool_diagnostic_log_file, & +! log_levelpool_diagnostic_data +!#endif + + implicit none + + ! Extend/derive level pool type from the abstract base + ! type for reservoirs. + type, extends(reservoir) :: levelpool + + ! Define pointers to sub-types / sub-objects to and + ! held by a level pool reservoir object. + type (levelpool_properties_interface), pointer :: properties => null() + type (levelpool_state_interface), pointer :: state => null() + + logical :: pointer_allocation_guard = .false. + + contains + + procedure :: init => levelpool_init + procedure :: destroy => levelpool_destroy + procedure :: run => run_levelpool_reservoir + + end type levelpool + +contains + + !Level Pool Constructor + subroutine levelpool_init(this, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, max_depth, lake_number) + implicit none + class(levelpool), intent(inout) :: this ! object being initialized + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: max_depth ! max depth of reservoir before overtop (meters) + integer, intent(in) :: lake_number ! lake number + character(len=15) :: lake_number_string + +!#ifdef RESERVOIR_D + ! Create diagnostic log file only for development/debugging purposes + !call create_levelpool_diagnostic_log_file(lake_number) +!#endif + + if (this%pointer_allocation_guard .eqv. .false. ) then + ! try to allocate input + allocate ( this%input ) + if ( .not. associated(this%input) ) then + ! if the input structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + !call hydro_stop("ERROR: Failure to allocate level pool input structure for reservoir " & + !// trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the input structure + call this%input%init() + end if + + ! try to allocate output + allocate ( this%output ) + if ( .not. associated(this%output) ) then + ! if the output structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + !call hydro_stop("ERROR: Failure to allocate level pool output structure for reservoir " & + !// trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the output structure + call this%output%init() + end if + + ! try to allocate properties + allocate ( this%properties ) + if ( .not. associated(this%properties) ) then + ! if the properties structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + !call hydro_stop("ERROR: Failure to allocate levelpool properties structure for reservoir " & + !// trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize levelpool properties + call this%properties%init( lake_area, & + weir_elevation, weir_coeffecient, weir_length, dam_length, & + orifice_elevation, orifice_coefficient, & + orifice_area, max_depth, lake_number ) + end if + this%pointer_allocation_guard = .true. + + ! try to allocate state + allocate ( this%state ) + if ( .not. associated(this%state) ) then + ! if the state structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + !call hydro_stop("ERROR: Failure to allocate state properties structure for reservoir " & + !// trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize levelpool state + call this%state%init( water_elevation ) + end if + this%pointer_allocation_guard = .true. + end if + + end subroutine levelpool_init + + + !Level Pool Destructor + subroutine levelpool_destroy(this) + implicit none + class(levelpool), intent(inout) :: this ! object being destroyed + end subroutine levelpool_destroy + + + ! Subroutine for running a level pool reservoir, + ! which will then call the LEVELPOOL method/subroutine for processing the + ! inputs and returning the output. + subroutine run_levelpool_reservoir(this, previous_timestep_inflow, inflow, & + lateral_inflow, water_elevation, outflow, routing_period, dynamic_reservoir_type, & + assimilated_value, assimilated_source_file) + implicit none + class(levelpool), intent(inout) :: this + real, intent(in) :: previous_timestep_inflow ! cubic meters per second (cms) + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer, intent(out):: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real, intent(out) :: assimilated_value ! value assimilated from observation or forecast + character(len=256), intent(out) :: assimilated_source_file ! source file of assimilated value + + + ! Update input variables + this%input%inflow = inflow + this%input%lateral_inflow = lateral_inflow + + ! Update state variables + this%state%water_elevation = water_elevation + + call LEVELPOOL_PHYSICS(this%properties%lake_number, & + previous_timestep_inflow, & + this%input%inflow, & + this%output%outflow, & + this%input%lateral_inflow, & + routing_period, & + this%state%water_elevation, & + this%properties%lake_area, & + this%properties%weir_elevation, & + this%properties%max_depth, & + this%properties%weir_coeffecient, & + this%properties%weir_length, & + this%properties%dam_length, & + this%properties%orifice_elevation, & + this%properties%orifice_coefficient, & + this%properties%orifice_area & + ) + + ! Update output variable returned from this subroutine + outflow = this%output%outflow + + ! Set current inflow to previous_timestep_inflow + this%input%previous_timestep_inflow = inflow + + ! Update water_elevation variable returned from this subroutine + water_elevation = this%state%water_elevation + + ! The dynamic reservoir type is always set to 1 for level pool in this module because + ! it cannot change reservoir types + dynamic_reservoir_type = 1 + + ! The assimilated value would always be a sentinel of -9999.0 because level pool does + ! not assimilate any external observations or forecasts + assimilated_value = -9999.0 + + ! The assimilated source file would always be an empty string + assimilated_source_file = "" + +#ifdef RESERVOIR_D + ! Log diagnostic data only for development/debugging purposes + !call log_levelpool_diagnostic_data(this%properties%lake_number, inflow, water_elevation, outflow) +#endif + + end subroutine run_levelpool_reservoir + + ! ------------------------------------------------ + ! SUBROUTINE LEVELPOOL + ! ------------------------------------------------ + + subroutine LEVELPOOL_PHYSICS(ln,qi0,qi1,qo1,ql,dt,H,ar,we,maxh,wc,wl,dl,oe,oc,oa) + + !! ---------------------------- argument variables + !! All elevations should be relative to a common base (often belev(k)) + + real, intent(INOUT) :: H ! water elevation height (m) + real, intent(IN) :: dt ! routing period [s] + real, intent(IN) :: qi0 ! inflow at previous timestep (cms) + real, intent(IN) :: qi1 ! inflow at current timestep (cms) + real, intent(OUT) :: qo1 ! outflow at current timestep + real, intent(IN) :: ql ! lateral inflow + real, intent(IN) :: ar ! area of reservoir (km^2) + real, intent(IN) :: we ! bottom of weir elevation + real, intent(IN) :: wc ! weir coeff. + real, intent(IN) :: wl ! weir length (m) + real, intent(IN) :: dl ! dam length(m) + real, intent(IN) :: oe ! orifice elevation + real, intent(IN) :: oc ! orifice coeff. + real, intent(IN) :: oa ! orifice area (m^2) + real, intent(IN) :: maxh ! max depth of reservoir before overtop (m) + integer, intent(IN) :: ln ! lake number + + !!DJG Add lake option switch here...move up to namelist in future versions... + integer :: LAKE_OPT ! Lake model option (move to namelist later) + real :: Htmp ! Temporary assign of incoming lake el. (m) + + !! ---------------------------- local variables + real :: sap ! local surface area values + real :: discharge ! storage discharge m^3/s + real :: tmp1, tmp2 + real :: dh, dh1, dh2, dh3 ! Depth in weir, and height function for 3 order RK + real :: It, Itdt_3, Itdt_2_3 ! inflow hydrographs + real :: maxWeirDepth !maximum capacity of weir + !real :: hdiff_vol, qdiff_vol ! water balance check variables + !! ---------------------------- subroutine body: from chow, mad mays. pg. 252 + !! -- determine from inflow hydrograph + + + !!DJG Set hardwire for LAKE_OPT...move specification of this to namelist in + !future versions... + LAKE_OPT = 2 + Htmp = H !temporary set of incoming lake water elevation... + !hdiff_vol = 0.0 + !qdiff_vol = 0.0 + + !!DJG IF-block for lake model option 1 - outflow=inflow, 2 - Chow et al level + !pool, ..... + if (LAKE_OPT == 1) then ! If-block for simple pass through scheme.... + + qo1 = qi1 ! Set outflow equal to inflow at current time + H = Htmp ! Set new lake water elevation to incoming lake el. + + else if (LAKE_OPT == 2) then ! If-block for Chow et al level pool scheme + + It = qi0 + Itdt_3 = qi0 + ((qi1 + ql - qi0) * 0.33) + Itdt_2_3 = qi0 + ((qi1 + ql - qi0) * 0.67) + maxWeirDepth = maxh - we + + !assume vertically walled reservoir + !remove this when moving to a variable head area volume + sap = ar * 1.0E6 + + !-- determine Q(dh) from elevation-discharge relationship + !-- and dh1 + dh = H - we + if (dh > maxWeirDepth) then + dh = maxWeirDepth + endif + + tmp1 = oc * oa * sqrt(2. * 9.81 * ( H - oe )) !orifice at capacity + tmp2 = wc * wl * (dh ** (3./2.)) !weir flows at capacity + + !determine the discharge based on current height + if(H > maxh) then + discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(3./2.)) !overtop + else if (dh > 0.0 ) then !! orifice and weir discharge + discharge = tmp1 + tmp2 + else if ( H > oe ) then !! only orifice flow + discharge = oc * oa * sqrt(2. * 9.81 * ( H - oe ) ) + else + discharge = 0.0 !in the dead pool + endif + + if (sap > 0) then + dh1 = ((It - discharge)/sap)*dt + else + dh1 = 0.0 + endif + + !-- determine Q(H + dh1/3) from elevation-discharge relationship + !-- dh2 + dh = (H+dh1/3) - we + if (dh > maxWeirDepth) then + dh = maxWeirDepth + endif + + tmp1 = oc * oa * sqrt(2. * 9.81 * ( (H+dh1/3.) - oe ) ) + tmp2 = wc * wl * (dh ** (3./2.)) + + !determine the discharge based on current height + if(H > maxh) then + discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(3./2.)) !overtop + else if (dh > 0.0 ) then !! orifice and weir discharge + discharge = tmp1 + tmp2 + else if ( (H+dh1/3) > oe ) then !! only orifice flow,not full + discharge = oc * oa * sqrt(2. * 9.81 * ( (H+dh1/3.) - oe ) ) + else + discharge = 0.0 + endif + + + if (sap > 0.0) then + dh2 = ((Itdt_3 - discharge)/sap)*dt + else + dh2 = 0.0 + endif + + !-- determine Q(H + 2/3 dh2) from elevation-discharge relationship + !-- dh3 + dh = (H + (0.667*dh2)) - we + if (dh > maxWeirDepth) then + dh = maxWeirDepth + endif + + tmp1 = oc * oa * sqrt(2. * 9.81 * ( (H+dh2*0.667) - oe ) ) + tmp2 = wc * wl * (dh ** (3./2.)) + + !determine the discharge based on current height + if(H > maxh) then ! overtop condition, not good! + discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(3./2.)) !overtop + else if (dh > 0.0 ) then !! orifice and weir discharge + discharge = tmp1 + tmp2 + else if ( (H+dh2*0.667) > oe ) then !! only orifice flow,not full + discharge = oc * oa * sqrt(2. * 9.81 * ( (H+dh2*0.667) - oe ) ) + else + discharge = 0.0 + endif + + if (sap > 0.0) then + dh3 = ((Itdt_2_3 - discharge)/sap)*dt + else + dh3 = 0.0 + endif + + !-- determine dh and H + dh = (dh1/4.) + (0.75*dh3) + H = H + dh + + !-- compute final discharge + dh = H - we + if (dh > maxWeirDepth) then + dh = maxWeirDepth + endif + + tmp1 = oc * oa * sqrt(2. * 9.81 * ( H - oe ) ) + tmp2 = wc * wl * (dh ** (3./2.)) + + !determine the discharge based on current height + if(H > maxh) then ! overtop condition, not good! + discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(3./2.)) !overtop + else if (dh > 0.0 ) then !! orifice and overtop discharge + discharge = tmp1 + tmp2 + else if ( H > oe ) then !! only orifice flow,not full + discharge = oc * oa * sqrt(2. * 9.81 * ( H - oe ) ) + else + discharge = 0.0 + endif + + qo1 = discharge ! return the flow rate from reservoir + + !#ifdef HYDRO_D + !#ifndef NCEP_WCOSS + ! ! Water balance check + ! qdiff_vol = (qi1+ql-qo1)*dt !m3 + ! hdiff_vol = (H-Htmp)*sap !m3 + !22 format(f8.4,2x,f8.4,2x,f8.4,2x,f8.4,2x,f8.4,2x,f6.0,2x,f20.1,2x,f20.1) + ! open (unit=67, & + ! file='lake_massbalance_out.txt', status='unknown',position='append') + ! write(67,22) Htmp, H, qi1, ql, qo1, dt, qdiff_vol, hdiff_vol + ! close(67) + !#endif + !#endif + + !23 format('botof H dh orf wr Q',f8.4,2x,f8.4,2x,f8.3,2x,f8.3,2x,f8.2) + !24 format('ofonl H dh sap Q ',f8.4,2x,f8.4,2x,f8.0,2x,f8.2) + + + else ! ELSE for LAKE_OPT.... + endif ! ENDIF for LAKE_OPT.... + + return + + ! ---------------------------------------------------------------- + end subroutine LEVELPOOL_PHYSICS + ! ---------------------------------------------------------------- + +end module module_levelpool diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_properties.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_properties.F new file mode 100644 index 000000000..3e8329018 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_properties.F @@ -0,0 +1,72 @@ +! This module defines and instantiates objects +! for a level pool type reservoir's +! parameters/properties. Properties holds +! static/unchanging variables that are +! set when the given reservoir object is +! initialized/instantiated. +module module_levelpool_properties + + use module_reservoir, only: reservoir_properties + implicit none + + ! Extend/derive level pool properties from the abstract base + ! type for reservoir properties. + type, extends(reservoir_properties) :: levelpool_properties_interface + real :: lake_area ! area of lake (km^2) + real :: weir_elevation ! bottom of weir elevation (meters AMSL) + real :: weir_coeffecient ! weir coefficient + real :: weir_length ! weir length (meters) + real :: dam_length ! dam length (meters) + real :: orifice_elevation ! orifice elevation (meters AMSL) + real :: orifice_coefficient ! orifice coefficient + real :: orifice_area ! orifice area (meters^2) + real :: max_depth ! max depth of reservoir before overtop (meters) + integer :: lake_number ! lake number + + contains + + procedure :: init => levelpool_properties_init + procedure :: destroy => levelpool_properties_destroy + + end type levelpool_properties_interface + +contains + + !Level Pool Properties Constructor + subroutine levelpool_properties_init(this, lake_area, & + weir_elevation, weir_coeffecient, weir_length, dam_length, orifice_elevation, & + orifice_coefficient, orifice_area, max_depth, lake_number) + implicit none + class(levelpool_properties_interface), intent(inout) :: this ! the type object being initialized + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: max_depth ! max depth of reservoir before overtop (meters) + integer, intent(in) :: lake_number ! lake number + + ! Assign the values passed in to a particular level pool reservoir + ! properties object's variables. + this%lake_area = lake_area + this%weir_elevation = weir_elevation + this%weir_coeffecient = weir_coeffecient + this%weir_length = weir_length + this%orifice_elevation = orifice_elevation + this%orifice_coefficient = orifice_coefficient + this%orifice_area = orifice_area + this%max_depth = max_depth + this%lake_number = lake_number + this%dam_length = dam_length + end subroutine levelpool_properties_init + + !Level Pool Properties Destructor + subroutine levelpool_properties_destroy(this) + implicit none + class(levelpool_properties_interface), intent(inout) :: this ! the type object being destroyed + end subroutine levelpool_properties_destroy + +end module module_levelpool_properties diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_state.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_state.F new file mode 100644 index 000000000..98724de8a --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_state.F @@ -0,0 +1,45 @@ +! This module defines and instantiates objects +! for a level pool type reservoir's state. +! State holds and tracks dynamic/changing variables +! that are only relevant to the given level pool +! reservoir object and not other modules or areas +! of the system. +module module_levelpool_state + + use module_reservoir, only: reservoir_state + implicit none + + ! Extend/derive level pool state from the abstract base + ! type for reservoir state. + type, extends(reservoir_state) :: levelpool_state_interface + real :: water_elevation ! meters AMSL + + contains + + procedure :: init => levelpool_state_init + procedure :: destroy => levelpool_state_destroy + + end type levelpool_state_interface + +contains + + !Level Pool State Constructor + subroutine levelpool_state_init(this, water_elevation) + implicit none + class(levelpool_state_interface), intent(inout) :: this ! the type object being initialized + real, intent(inout) :: water_elevation ! meters AMSL + + ! Assign the water elevation value passed in to a particular level pool reservoir + ! state object's variable for water elevation + this%water_elevation = water_elevation + + end subroutine levelpool_state_init + + !Level Pool State Destructor + subroutine levelpool_state_destroy(this) + implicit none + class(levelpool_state_interface), intent(inout) :: this ! the type object being destroyed + + end subroutine levelpool_state_destroy + +end module module_levelpool_state diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_tests.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_tests.F new file mode 100644 index 000000000..ff3f8a55d --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool_tests.F @@ -0,0 +1,256 @@ +! This module holds tests for initialization of +! various attributes of a levelpool reservoir. +module module_levelpool_tests + use module_levelpool + +contains + + function levelpool_data_info(levelpool_data) result(data_info_result) + implicit none + type (levelpool) :: levelpool_data + logical :: data_info_result + logical, dimension(4) :: ptr_state + logical, dimension(4) :: data_state + + data_info_result = .false. + + ! Check to see if the levelpool_state data structure exists + print *, "Checking pointer association on data%state " + if ( associated(levelpool_data%state) ) then + print *, "PASSED" + ptr_state(1) = .true. + else + print *, "FAILED" + ptr_state(1) = .false. + end if + + ! Check to see if the levelpool_properties data structure exists + print *, "Checking pointer association on data%properties " + if ( associated(levelpool_data%properties) ) then + print *, "PASSED" + ptr_state(2) = .true. + else + print *, "FAILED" + ptr_state(2) = .false. + end if + + ! Check to see if the levelpool_input data structure exists + print *, "Checking pointer association on data%input " + if ( associated(levelpool_data%input) ) then + print *, "PASSED" + ptr_state(3) = .true. + else + print *, "FAILED" + ptr_state(3) = .false. + end if + + ! Check to see if the levelpool_output data structure exists + print *, "Checking pointer association on data%output " + if ( associated(levelpool_data%output) ) then + print *, "PASSED" + ptr_state(4) = .true. + else + print *, "FAILED" + ptr_state(4) = .false. + end if + + ! Now check the data members of each substructure + if ( ptr_state(1) ) then + data_state(1) = test_levelpool_state(levelpool_data%state) + end if + + if ( ptr_state(2) ) then + data_state(2) = test_levelpool_properties(levelpool_data%properties) + end if + + if ( ptr_state(3) ) then + data_state(3) = test_input(levelpool_data%input) + end if + + if ( ptr_state(4) ) then + data_state(4) = test_output(levelpool_data%output) + end if + + + if ( all(ptr_state) .and. all(data_state) ) then + data_info_result = .true. + print *, "========================================================================" + print *, "All Level Pool Tests Passed" + print *, "========================================================================" + + else + data_info_result = .false. + print *, "========================================================================" + print *, "Not All Level Pool Tests Passed" + print *, "========================================================================" + end if + + end function levelpool_data_info + + + ! Test to see that each member of the input structure is correctly allocated and readable + function test_input(o) result(rv) + type (reservoir_input), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the input structure" + print *, " " + + print *, "Checking read on inflow" + print *, o%inflow + if ( o%inflow .lt. 0.0 - epsilon(0.0) .or. o%inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lateral_inflow" + print *, o%lateral_inflow + if ( o%lateral_inflow .lt. 0.0 - epsilon(0.0) .or. o%lateral_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on previous_timestep_inflow" + print *, o%previous_timestep_inflow + if ( o%previous_timestep_inflow .lt. 0.0 - epsilon(0.0) .or. o%previous_timestep_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_input + + ! Test to see that each member of the output structure is correctly allocated and readable + function test_output(o) result(rv) + type (reservoir_output), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the output structure" + print *, " " + + print *, "Checking read on outflow" + print *, o%outflow + if ( o%outflow .lt. 0.0 - epsilon(0.0) .or. o%outflow .gt. 0.0 + epsilon(0.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_output + + ! Test to see that each member of the state structure is correctly allocated and readable + function test_levelpool_state(o) result(rv) + type (levelpool_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the level pool state data structure" + print *, " " + + print *, "Checking read on water_elevation" + print *, o%water_elevation + if ( o%water_elevation .lt. 2.0 - epsilon(2.0) .or. o%water_elevation .gt. 2.0 + epsilon(2.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_levelpool_state + + + ! Test to see that each member of the properties structure is correctly allocated and readable + function test_levelpool_properties(o) result(rv) + type (levelpool_properties_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the level pool properties data structure" + print *, " " + + print *, "Checking read on lake_area" + print *, o%lake_area + if ( o%lake_area .lt. 4.0 - epsilon(4.0) .or. o%lake_area .gt. 4.0 + epsilon(4.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_elevation" + print *, o%weir_elevation + if ( o%weir_elevation .lt. 6.0 - epsilon(6.0) .or. o%weir_elevation .gt. 6.0 + epsilon(6.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_coeffecient" + print *, o%weir_coeffecient + if ( o%weir_coeffecient .lt. 8.0 - epsilon(8.0) .or. o%weir_coeffecient .gt. 8.0 + epsilon(8.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_length" + print *, o%weir_length + if ( o%weir_length .lt. 10.0 - epsilon(10.0) .or. o%weir_length .gt. 10.0 + epsilon(10.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_elevation" + print *, o%orifice_elevation + if ( o%orifice_elevation .lt. 12.0 - epsilon(12.0) .or. o%orifice_elevation .gt. 12.0 + epsilon(12.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_coefficient" + print *, o%orifice_coefficient + if ( o%orifice_coefficient .lt. 14.0 - epsilon(14.0) .or. o%orifice_coefficient .gt. 14.0 + epsilon(14.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_area" + print *, o%orifice_area + if ( o%orifice_area .lt. 16.0 - epsilon(16.0) .or. o%orifice_area .gt. 16.0 + epsilon(16.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on max_depth" + print *, o%max_depth + if ( o%max_depth .lt. 18.0 - epsilon(18.0) .or. o%max_depth .gt. 18.0 + epsilon(18.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lake_number" + print *, o%lake_number + if ( o%lake_number .ne. 20) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_levelpool_properties + +end module diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/CMakeLists.txt b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/CMakeLists.txt new file mode 100644 index 000000000..6a254ce15 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required (VERSION 2.8) + +add_library(hydro_routing_reservoirs_hybrid STATIC + module_persistence_levelpool_hybrid.F + module_persistence_levelpool_hybrid_state.F + module_persistence_levelpool_hybrid_properties.F +) + +add_dependencies(hydro_routing_reservoirs_hybrid hydro_routing_reservoirs) + + + diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/Makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/Makefile new file mode 100644 index 000000000..ade00b1ee --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/Makefile @@ -0,0 +1,28 @@ + +include ../macros + +MODFLAG := -I ../mod + +%.o : %.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) $< + +.PHONY: all mod test + +all: mod + +mod: + #Build each sub module then build the module that depends on all sub modules + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_persistence_levelpool_hybrid_properties.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_persistence_levelpool_hybrid_state.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_persistence_levelpool_hybrid.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_persistence_levelpool_hybrid_tests.F + ar -r ../lib/libHYDRO.a module_persistence_levelpool_hybrid_properties.o + ar -r ../lib/libHYDRO.a module_persistence_levelpool_hybrid_state.o + ar -r ../lib/libHYDRO.a module_persistence_levelpool_hybrid.o + ar -r ../lib/libHYDRO.a module_persistence_levelpool_hybrid_tests.o + + cp *.mod ../mod + +clean: + rm -f *.o + rm -f *.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid.F new file mode 100644 index 000000000..7ae5ce0fd --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid.F @@ -0,0 +1,450 @@ +! This module defines and instantiates objects +! for a hybrid persistence levelpool type +! reservoir. The hybrid reservoir type +! inherits input and output types from the +! reservoir base module and calls instantiation +! of these into sub-objects. The hybrid +! reservoir type also points to types for +! hybrid properties and state and calls +! instantiation of these into sub-objects. +! A pointer to a levelpool reservoir object +! is also held in state, and this module +! instantiates that levelpool object. There +! is also a subroutine to run hybrid reservoir +! that is derived from the reservoir base +! type interface to run reservoir. + +module module_persistence_levelpool_hybrid + + use module_persistence_levelpool_hybrid_properties, only: hybrid_properties_interface + use module_persistence_levelpool_hybrid_state, only: hybrid_state_interface + use module_levelpool, only: levelpool + use module_reservoir_utilities, only: modify_for_projected_storage, warn_negative_inflow, & + create_hybrid_diagnostic_log_file, & + log_hybrid_diagnostic_data + use module_reservoir, only: reservoir, reservoir_input, reservoir_output + use module_reservoir_read_timeslice_data, only: usgs_timeslice_data, usace_timeslice_data, timeslice_data_type + use module_hydro_stop, only: HYDRO_stop + implicit none + + ! Extend/derive hybrid type from the abstract base + ! type for reservoirs. + type, extends(reservoir) :: persistence_levelpool_hybrid + + ! Define pointers to sub-types / sub-objects to and + ! held by a level pool reservoir object. + type (hybrid_properties_interface), pointer :: properties => null() + type (hybrid_state_interface), pointer :: state => null() + type (timeslice_data_type), pointer :: timeslice_data => null() + + logical :: pointer_allocation_guard = .false. + + contains + + procedure :: init => hybrid_init + procedure :: destroy => hybrid_destroy + procedure :: run => run_hybrid_reservoir + + end type persistence_levelpool_hybrid + +#ifndef NCEP_WCOSS + integer, parameter :: log_warning = 6 +#else + integer, parameter :: log_warning = 78 +#endif + +contains + + ! Hybrid Constructor + subroutine hybrid_init(this, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, & + lake_number, reservoir_type, reservoir_parameter_file, start_date, & + usgs_timeslice_path, usace_timeslice_path, observation_lookback_hours, & + observation_update_time_interval_seconds) + implicit none + class(persistence_levelpool_hybrid), intent(inout) :: this ! object being initialized + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + integer, intent(in) :: lake_number ! lake number + integer, intent(in) :: reservoir_type ! reservoir type + character(len=*), intent(in) :: reservoir_parameter_file + character(len=19), intent(in) :: start_date + character(len=*), intent(in) :: usgs_timeslice_path + character(len=*), intent(in) :: usace_timeslice_path + integer, intent(in) :: observation_lookback_hours + integer, intent(in) :: observation_update_time_interval_seconds + character(len=15) :: lake_number_string + character(len=15) :: reservoir_type_string + +#ifdef RESERVOIR_D + ! Create diagnostic log file only for development/debugging purposes + call create_hybrid_diagnostic_log_file(lake_number) +#endif + + if (this%pointer_allocation_guard .eqv. .false. ) then + + ! Call to initialize timeslice data object. This object is a singleton for either + ! USGS (U.S. Geological Survey) or USACE (U.S. Army Corps of Engineers) data source, + ! so if another reservoir has already initialized the object of a particular data + ! source, it will return immediately. + if (reservoir_type == 2) then + call usgs_timeslice_data%init(start_date, usgs_timeslice_path, & + reservoir_parameter_file, "usgs", observation_lookback_hours) + + ! Assign pointer to usgs_timeslice_data object + this%timeslice_data=>usgs_timeslice_data + + else if (reservoir_type == 3) then + call usace_timeslice_data%init(start_date, usace_timeslice_path, & + reservoir_parameter_file, "usace", observation_lookback_hours) + + ! Assign pointer to usace_timeslice_data object + this%timeslice_data=>usace_timeslice_data + + else + ! Call hydro_stop if reservoir_type is not USGS or USACE because the + ! data source is not available. + write(lake_number_string, "(I15)") lake_number + write(reservoir_type_string, "(I15)") reservoir_type + call hydro_stop("ERROR: Incorrect reservoir type for reservoir " // trim(ADJUSTL(lake_number_string)) // & + ". Expected reservoir type 2 or 3 and instead received reservoir type " & + // trim(ADJUSTL(reservoir_type_string)) // "." ) + end if + + ! try to allocate input + allocate ( this%input ) + if ( .not. associated(this%input) ) then + ! if the input structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate hybrid input structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the input structure + call this%input%init() + end if + + ! try to allocate output + allocate ( this%output ) + if ( .not. associated(this%output) ) then + ! if the output structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate hybrid output structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the output structure + call this%output%init() + end if + + ! try to allocate properties + allocate ( this%properties ) + if ( .not. associated(this%properties) ) then + ! if the properties structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate hybrid properties structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize hybrid properties + call this%properties%init(lake_area, lake_max_water_elevation, orifice_elevation, lake_number, & + reservoir_type, observation_lookback_hours, observation_update_time_interval_seconds, & + reservoir_parameter_file) + end if + this%pointer_allocation_guard = .true. + + ! try to allocate state + allocate ( this%state ) + if ( .not. associated(this%state) ) then + ! if the state structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate hybrid state structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize hybrid state + call this%state%init(water_elevation, lake_area, lake_max_water_elevation, orifice_elevation, & + initial_fractional_depth, reservoir_type) + end if + this%pointer_allocation_guard = .true. + + ! Initialize persistence weight index to be out of bounds of the persistence weights array + this%state%persistence_weight_index = SIZE(this%properties%persistence_weighted_coefficients) + 1 + + ! Output warning if the initial storage is greater than max storage or less than zero. + if (this%state%current_storage > this%properties%max_storage) then + this%state%current_storage = this%properties%max_storage + write(lake_number_string, "(I15)") lake_number + write(log_warning,*) "WARNING: initial storage exceeds max storage for reservoir ", lake_number_string, & + ". Setting initial storage to max storage." + + else if (this%state%current_storage < this%properties%min_storage) then + this%state%current_storage = this%properties%min_storage + write(lake_number_string, "(I15)") lake_number + write(log_warning,*) "WARNING: initial storage is less than zero for reservoir ", lake_number_string, & + ". Setting initial storage to zero." + + end if + + ! Allocate a single level pool reservoir + allocate(levelpool :: this%state%levelpool_ptr) + + ! Initialize level pool reservoir + call this%state%levelpool_ptr%init(water_elevation, lake_area, & + weir_elevation, weir_coeffecient, weir_length, dam_length, orifice_elevation, & + orifice_coefficient, orifice_area, lake_max_water_elevation, lake_number) + + end if + end subroutine hybrid_init + + + ! Hybrid Destructor + subroutine hybrid_destroy(this) + implicit none + class(persistence_levelpool_hybrid), intent(inout) :: this ! object being destroyed + end subroutine hybrid_destroy + + + ! Subroutine for running reservoir for a hybrid reservoir + subroutine run_hybrid_reservoir(this, previous_timestep_inflow, inflow, & + lateral_inflow, water_elevation, outflow, routing_period, dynamic_reservoir_type, & + assimilated_value, assimilated_source_file) + implicit none + class(persistence_levelpool_hybrid), intent(inout) :: this + real, intent(in) :: previous_timestep_inflow ! cubic meters per second (cms) + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer, intent(out):: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real, intent(out) :: assimilated_value ! value assimilated from observation + character(len=256), intent(out) :: assimilated_source_file ! source file of assimilated value + real :: delta_storage ! timestep change in storage (cubic meters) + real :: local_water_elevation ! water elevation passed to levelpool (meters AMSL) + real :: levelpool_outflow ! cubic meters per second (cms) + logical :: max_storage_reached ! flag for when max storage is reached + integer :: gage_lookback_seconds ! seconds between current model + ! time and the time a gage + ! discharge is read + + max_storage_reached = .false. + gage_lookback_seconds = 0 + + ! Update input variables + this%input%inflow = inflow + this%input%lateral_inflow = lateral_inflow + + ! The initialization and management of water elevation might diverge between the global state of the model + ! and the internal state. The global state water elevation is passed to/from this subroutine at every + ! timestep. This check is to help guard against an external piece of code adjusting the global state water + ! elevation, which would cause it to no longer be synchronized with internal state water elevation. This + ! check also allows this module to borrow from the existing restart capability by initializing reservoirs + ! to some default water elevation and storage and then waiting for the first timestep to receive the + ! initial condition, so that the reservoir would not need to manage its own restart. If water elevation + ! is refactored to no longer be a global state, then this check is not necessary. + if (this%state%water_elevation .ne. water_elevation) then + + ! Update state variables + this%state%water_elevation = water_elevation + + this%state%current_storage = (this%state%water_elevation - this%properties%orifice_elevation) * & + this%properties%lake_area + + end if + + local_water_elevation = this%state%water_elevation + + ! If update time to read new timeslice gage discharges. In forecast modes, the update time automatically is set to + ! 1,000,000 to ensure that gage discharges are only read at the first timestep. For analysis runs, a timeslice + ! update time is set as a namelist parameter. A typical update time for an analysis run would be 3600 seconds + ! (1 hour), at which time new observations would be retrieved. The first reservoir for each processor to reach an + ! update time will call the function to read a new timeslice file, and these timeslice discharges will be held in + ! memory for the subsequent reservoirs to use at that timestep. + if (this%state%current_time >= this%state%timeslice_update_time) then + + ! Read new timeslice gage discharges + call this%timeslice_data%setup_read_timeslice(this%properties%observation_update_time_interval_seconds, & + this%state%current_time, this%properties%gage_id, gage_lookback_seconds, this%state%gage_discharge, & + this%state%assimilated_source_file) + + ! If no good quality gage discharge was found in the given lookback period, the gage discharge is returned + ! as -1.0. The persistence weight index will be set out of bounds larger than the array. This causes the + ! the weight update logic to set the persistence weight to 0.0. Therefore, only levelpool calculations will be used. + if (this%state%gage_discharge < 0.0) then + + ! If at weight update time, then increment persistence weight index. + ! Otherwise, continue using previous persistence weight index. + if (this%state%current_time >= this%state%weight_update_time) then + ! Increment weight index + this%state%persistence_weight_index = this%state%persistence_weight_index + 1 + + ! Set next weight update time + this%state%weight_update_time = this%state%weight_update_time + this%properties%weight_update_time_interval + end if + + ! If out of bounds of array, then set persistence weight to 0.0. Otherwise, set persistence weight from array's indexed value. + if (this%state%persistence_weight_index > SIZE(this%properties%persistence_weighted_coefficients)) then + this%state%persistence_current_weight = 0.0 + + else + this%state%persistence_current_weight = this%properties%persistence_weighted_coefficients(this%state%persistence_weight_index) + end if + + else + + ! Set persisted outflow to gage discharge + this%state%persisted_outflow = this%state%gage_discharge + + ! Set assimilated value to gage discharge + this%state%assimilated_value = this%state%gage_discharge + + ! Start persistence weight index at 1 + this%state%persistence_weight_index = 1 + + ! Grab first persistence weight at index 1 from array + this%state%persistence_current_weight = this%properties%persistence_weighted_coefficients(this%state%persistence_weight_index) + + ! Set weight update time offset by how long back a timeslice discharge was read. For instance, if the weight update interval is + ! 24 hours, and the gage discharge was read 2 hours back, then the weight update time will be set to 22 hours after the current time. + this%state%weight_update_time = this%state%current_time + this%properties%weight_update_time_interval - gage_lookback_seconds + + end if + + ! Calculate levelpool weight + this%state%levelpool_current_weight = 1.0 - this%state%persistence_current_weight + + ! Set timeslice update time + this%state%timeslice_update_time = this%state%timeslice_update_time + this%properties%observation_update_time_interval_seconds + + ! If update time to change persistence weights + else if (this%state%current_time >= this%state%weight_update_time) then + + ! Increment weight index + this%state%persistence_weight_index = this%state%persistence_weight_index + 1 + + ! Boundary check to not exceed the size of the persistence weights array + if (this%state%persistence_weight_index <= SIZE(this%properties%persistence_weighted_coefficients)) then + + ! Grab indexed persistence weight from array + this%state%persistence_current_weight = this%properties%persistence_weighted_coefficients(this%state%persistence_weight_index) + + else + ! If boundary of persistence weights array has been exceeded, then set all persistence weights to 0.0 + this%state%persistence_current_weight = 0.0 + + end if + + ! Calculate levelpool weight + this%state%levelpool_current_weight = 1.0 - this%state%persistence_current_weight + + ! Set next weight update time + this%state%weight_update_time = this%state%weight_update_time + this%properties%weight_update_time_interval + + end if + + ! Referencing issue https://github.com/NCAR/wrf_hydro_nwm_public/issues/326 for the uncertainty about + ! previous_timestep_inflow. Further understanding of the exact timing of inflow and previous timestep + ! inflow might affect which values are passed to this calling of levelpool and subsequent mass/balance + ! calculations. + ! Run levelpool reservoir + call this%state%levelpool_ptr%run(previous_timestep_inflow, inflow, & + lateral_inflow, local_water_elevation, levelpool_outflow, routing_period, this%state%levelpool_reservoir_type, & + this%state%levelpool_assimilated_value, this%state%levelpool_assimilated_source_file) + + ! If the levelpool weight is within epsilon of 1.0 + if (this%state%levelpool_current_weight .ge. 1.0 - epsilon(1.0)) then + + ! Set dynamic_reservoir_type to levelpool type + this%state%dynamic_reservoir_type = this%state%levelpool_reservoir_type + + ! Set the assimilated_value to sentinel, -9999.0 + this%state%assimilated_value = -9999.0 + + ! Set the assimilated_source_file to empty string + this%state%assimilated_source_file = "" + else + + ! Set dynamic_reservoir_type to given USGS or USACE type + this%state%dynamic_reservoir_type = this%properties%reservoir_type + end if + + ! Calculate outflow weighted between persistence and levelpool + this%output%outflow = this%state%persistence_current_weight & + * this%state%persisted_outflow + this%state%levelpool_current_weight & + * levelpool_outflow + + ! Warn if there is a negative inflow + call warn_negative_inflow(this%input%inflow, this%properties%lake_number, this%state%current_time) + + ! Modify if exceeding storage boundary conditions + call modify_for_projected_storage(this%input%inflow, & + this%state%current_storage, this%properties%min_storage, & + this%properties%max_storage, this%properties%lake_number, & + this%state%current_time, int(routing_period), this%output%outflow, max_storage_reached) + + ! If max storage has been reached, release greater of persistence or levelpool + if (max_storage_reached .and. this%output%outflow < levelpool_outflow) then + this%output%outflow = levelpool_outflow + + ! Set dynamic_reservoir_type to levelpool type + this%state%dynamic_reservoir_type = this%state%levelpool_reservoir_type + + ! Set the assimilated_value to sentinel, -9999.0 + this%state%assimilated_value = -9999.0 + + ! Set the assimilated_source_file to empty string + this%state%assimilated_source_file = "" + end if + + ! Calculate change in storage + delta_storage = (this%input%inflow - this%output%outflow) * routing_period + + ! Update storage from the most recent model states + this%state%current_storage = this%state%current_storage + delta_storage + + ! Calculate new water elevation. Delta storage is used as opposed to calculating from current storage in + ! order to minimize floating point error because current storage is a much larger magnitude than water elevation. + this%state%water_elevation = this%state%water_elevation + delta_storage / this%properties%lake_area + + ! Update output variable returned from this subroutine + outflow = this%output%outflow + + ! Set current inflow to previous_timestep_inflow + this%input%previous_timestep_inflow = inflow + + ! Update water_elevation variable returned from this subroutine + water_elevation = this%state%water_elevation + + ! Update the dynamic_reservoir_type returned from this subroutine + dynamic_reservoir_type = this%state%dynamic_reservoir_type + + ! Update the assimilated_value returned from this subroutine + assimilated_value = this%state%assimilated_value + + ! Update the assimilated_source_file returned from this subroutine + assimilated_source_file = this%state%assimilated_source_file + + ! Update the current time + this%state%current_time = this%state%current_time + int(routing_period) + +#ifdef RESERVOIR_D + ! Log diagnostic data only for development/debugging purposes + call log_hybrid_diagnostic_data(this%properties%lake_number, this%state%current_time, & + this%state%timeslice_update_time, this%state%weight_update_time, gage_lookback_seconds, & + this%properties%gage_id, this%state%persistence_weight_index, & + this%state%persistence_current_weight, this%input%inflow, this%state%current_storage, & + this%state%water_elevation, this%state%gage_discharge, this%state%persisted_outflow, & + levelpool_outflow, this%output%outflow) +#endif + + end subroutine run_hybrid_reservoir + +end module module_persistence_levelpool_hybrid diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_properties.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_properties.F new file mode 100644 index 000000000..4f82b443b --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_properties.F @@ -0,0 +1,139 @@ +! This module defines and instantiates objects +! for a hybrid type reservoir's +! parameters/properties. Properties holds +! static/unchanging variables that are +! set when the given reservoir object is +! initialized/instantiated. + +module module_persistence_levelpool_hybrid_properties + use module_reservoir_utilities, only: read_netcdf_lake_id, & + read_persistence_netcdf_gage_id, & + read_persistence_netcdf_real_2D_parameters, & + handle_err + use module_reservoir, only: reservoir_properties + use netcdf + implicit none + + ! Extend/derive hybrid properties from the abstract base + ! type for reservoir properties. + type, extends(reservoir_properties) :: hybrid_properties_interface + real :: min_storage ! minimum storage (cubic meters) + real :: max_storage ! maximum storage (cubic meters) + real :: lake_area ! area of reservoir (km^2) + real :: orifice_elevation ! orifice elevation (meters AMSL) + integer :: lake_number ! lake number + integer :: reservoir_type ! reservoir type + character(len=15) :: gage_id + integer :: observation_lookback_hours + integer :: observation_update_time_interval_seconds + integer :: weight_update_time_interval + real, allocatable, dimension(:) :: persistence_weighted_coefficients + + contains + + procedure :: init => hybrid_properties_init + procedure :: destroy => hybrid_properties_destroy + + end type hybrid_properties_interface + + integer, parameter :: seconds_in_day = 86400 + +contains + + ! Hybrid Properties Constructor + subroutine hybrid_properties_init(this, lake_area, lake_max_water_elevation, orifice_elevation, lake_number, & + reservoir_type, observation_lookback_hours, observation_update_time_interval_seconds, reservoir_parameter_file) + implicit none + class(hybrid_properties_interface), intent(inout) :: this ! the type object being initialized + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + integer, intent(in) :: lake_number ! lake number + integer, intent(in) :: reservoir_type ! reservoir type + integer, intent(in) :: observation_lookback_hours + integer, intent(in) :: observation_update_time_interval_seconds + character(len=*), intent(in) :: reservoir_parameter_file + integer :: ncid, var_id, lake_id_index + integer :: status ! status of reading NetCDF + integer :: number_of_weights, number_of_lakes + real, allocatable, dimension(:,:) :: temp_real_2D_array + + this%lake_area = lake_area * 1.0E6 + + this%orifice_elevation = orifice_elevation + + this%lake_number = lake_number + + this%reservoir_type = reservoir_type + + this%observation_lookback_hours = observation_lookback_hours + + this%observation_update_time_interval_seconds = observation_update_time_interval_seconds + + this%weight_update_time_interval = seconds_in_day + + this%min_storage = 0.0 + + this%max_storage = (lake_max_water_elevation - orifice_elevation) * lake_area * 1.0E6 + + ! Open Reservoir Parameter NetCDF file + status = nf90_open(path = reservoir_parameter_file, mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Read relevant properties from Reservoir Parameter NetCDF + ! If reservoir_type is 2, then look for USGS parameters + if (this%reservoir_type == 2) then + + call read_netcdf_lake_id(ncid, lake_number, "usgs_lake_id", reservoir_parameter_file, lake_id_index) + + call read_persistence_netcdf_gage_id(ncid, lake_id_index, "usgs_gage_id", reservoir_parameter_file, this%gage_id) + + call read_persistence_netcdf_real_2D_parameters(ncid, lake_id_index, "usgs_persistence_coefficients", & + reservoir_parameter_file, var_id, number_of_weights, number_of_lakes) + + allocate(this%persistence_weighted_coefficients(number_of_weights)) + + allocate(temp_real_2D_array(number_of_weights, number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_real_2D_array) + if (status /= nf90_noerr) call handle_err(status, "Error reading usgs_persistence_coefficients from " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! If reservoir_type is 3, then look for ACE parameters + else if (this%reservoir_type == 3) then + + call read_netcdf_lake_id(ncid, lake_number, "usace_lake_id", reservoir_parameter_file, lake_id_index) + + call read_persistence_netcdf_gage_id(ncid, lake_id_index, "usace_gage_id", reservoir_parameter_file, this%gage_id) + + call read_persistence_netcdf_real_2D_parameters(ncid, lake_id_index, "usace_persistence_coefficients", & + reservoir_parameter_file, var_id, number_of_weights, number_of_lakes) + + allocate(this%persistence_weighted_coefficients(number_of_weights)) + + allocate(temp_real_2D_array(number_of_weights, number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_real_2D_array) + if (status /= nf90_noerr) call handle_err(status, "Error reading usace_persistence_coefficients from " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + end if + + this%persistence_weighted_coefficients(:) = temp_real_2D_array(:, lake_id_index) + + if(allocated(temp_real_2D_array)) deallocate(temp_real_2D_array) + + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + end subroutine hybrid_properties_init + + ! Hybrid Properties Destructor + subroutine hybrid_properties_destroy(this) + implicit none + class(hybrid_properties_interface), intent(inout) :: this ! the type object being destroyed + end subroutine hybrid_properties_destroy + +end module module_persistence_levelpool_hybrid_properties diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_state.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_state.F new file mode 100644 index 000000000..4fcf1327d --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_state.F @@ -0,0 +1,108 @@ +! This module defines and instantiates objects +! for a hybrid type reservoir's state. +! State holds and tracks dynamic/changing variables +! that are only relevant to the given hybrid +! reservoir object and not other modules or areas +! of the system. + +module module_persistence_levelpool_hybrid_state + + use module_levelpool, only: levelpool + use module_reservoir, only: reservoir_state + implicit none + + ! Extend/derive hybrid state from the abstract base + ! type for reservoir state. + type, extends(reservoir_state) :: hybrid_state_interface + real :: water_elevation ! meters AMSL + real*4 :: current_storage ! cubic meters + real :: gage_discharge ! cubic meters per second (cms) + real :: persisted_outflow ! cubic meters per second (cms) + integer :: weight_update_time ! seconds + integer :: timeslice_update_time ! seconds + integer :: current_time ! seconds + integer :: persistence_weight_index + real :: levelpool_current_weight + real :: persistence_current_weight + integer :: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real :: assimilated_value + character(len=256) :: assimilated_source_file + integer :: levelpool_reservoir_type ! reservoir type for levelpool + real :: levelpool_assimilated_value ! levelpool assimilated sentinel value + character(len=256) :: levelpool_assimilated_source_file ! none for levelpool assimilated source + + type (levelpool), pointer :: levelpool_ptr ! pointer to levelpool object + + contains + + procedure :: init => hybrid_state_init + procedure :: destroy => hybrid_state_destroy + + end type hybrid_state_interface + + integer, parameter :: seconds_in_day = 86400 + +contains + + !Hybrid State Constructor + subroutine hybrid_state_init(this, water_elevation, lake_area, lake_max_water_elevation, orifice_elevation, & + initial_fractional_depth, reservoir_type) + implicit none + class(hybrid_state_interface), intent(inout) :: this ! the type object being initialized + real, intent(in) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + integer, intent(in) :: reservoir_type ! reservoir type + + ! Initialize the state water elevation in same manner as in module_RT.F + this%water_elevation = orifice_elevation + ((lake_max_water_elevation - orifice_elevation) * initial_fractional_depth) + + this%current_storage = (this%water_elevation - orifice_elevation) * lake_area * 1.0E6 + + this%gage_discharge = 0.0 + this%persisted_outflow = 0.0 + + this%weight_update_time = 0 + this%timeslice_update_time = 0 + this%current_time = 0 + + this%persistence_weight_index = 0 + this%levelpool_current_weight = 0.0 + this%persistence_current_weight = 0.0 + + ! If reservoir_type is set to 2 for USGS type, then set the dynamic_reservoir_type also to 2. + if (reservoir_type == 2) then + this%dynamic_reservoir_type = 2 + + ! If reservoir_type is set to 3 for USACE type, then set the dynamic_reservoir_type also to 3. + else if (reservoir_type == 3) then + this%dynamic_reservoir_type = 3 + end if + + ! Initialize to default sentinel, -9999.0 + this%assimilated_value = -9999.0 + + ! Initialize to default empty string + this%assimilated_source_file = "" + + ! Levelpool reservoir type set to 1 + this%levelpool_reservoir_type = 1 + + ! Set to default sentinel, -999.0 + this%levelpool_assimilated_value = -9999.0 + + ! Set to default string + this%levelpool_assimilated_source_file = "" + + end subroutine hybrid_state_init + + !Hybrid State Destructor + subroutine hybrid_state_destroy(this) + implicit none + class(hybrid_state_interface), intent(inout) :: this ! the type object being destroyed + + end subroutine hybrid_state_destroy + +end module module_persistence_levelpool_hybrid_state diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_tests.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_tests.F new file mode 100644 index 000000000..ae559d143 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Persistence_Level_Pool_Hybrid/module_persistence_levelpool_hybrid_tests.F @@ -0,0 +1,574 @@ +! This module holds tests for initialization of +! various attributes of a persistence levelpool +! hybrid reservoir. +module module_persistence_levelpool_hybrid_tests + use module_persistence_levelpool_hybrid + +contains + + ! Testing + function persistence_levelpool_hybrid_data_info(hybrid_data) result(rv) + implicit none + type (persistence_levelpool_hybrid) :: hybrid_data + logical :: rv + logical, dimension(8) :: ptr_state + logical, dimension(8) :: data_state + + rv = .false. + + ! Check to see if the hybrid_state data structure exists + print *, "Checking pointer association on hybrid_data%state " + if ( associated(hybrid_data%state) ) then + print *, "PASSED" + ptr_state(1) = .true. + else + print *, "FAILED" + ptr_state(1) = .false. + end if + print *, " " + + ! Check to see if the hybrid_properties data structure exists + print *, "Checking pointer association on hybrid_data%properties " + if ( associated(hybrid_data%properties) ) then + print *, "PASSED" + ptr_state(2) = .true. + else + print *, "FAILED" + ptr_state(2) = .false. + end if + print *, " " + + ! Check to see if the hybrid_input data structure exists + print *, "Checking pointer association on hybrid_data%input " + if ( associated(hybrid_data%input) ) then + print *, "PASSED" + ptr_state(3) = .true. + else + print *, "FAILED" + ptr_state(3) = .false. + end if + print *, " " + + ! Check to see if the hybrid_output data structure exists + print *, "Checking pointer association on hybrid_data%output " + if ( associated(hybrid_data%output) ) then + print *, "PASSED" + ptr_state(4) = .true. + else + print *, "FAILED" + ptr_state(4) = .false. + end if + print *, " " + + + ! Test levelpool structure + ! Check to see if the hybrid levelpool_state data structure exists + print *, "Checking pointer association on hybrid_data%levelpool_state for hybrid reservoirs" + if ( associated(hybrid_data%state%levelpool_ptr%state) ) then + print *, "PASSED" + ptr_state(5) = .true. + else + print *, "FAILED" + ptr_state(5) = .false. + end if + print *, " " + + ! Check to see if the hybrid levelpool_properties data structure exists + print *, "Checking pointer association on data%levelpool_properties for hybrid reservoirs" + if ( associated(hybrid_data%state%levelpool_ptr%properties) ) then + print *, "PASSED" + ptr_state(6) = .true. + else + print *, "FAILED" + ptr_state(6) = .false. + end if + print *, " " + + ! Check to see if the hybrid levelpool_input data structure exists + print *, "Checking pointer association on data%levelpool_input for hybrid reservoirs" + if ( associated(hybrid_data%state%levelpool_ptr%input) ) then + print *, "PASSED" + ptr_state(7) = .true. + else + print *, "FAILED" + ptr_state(7) = .false. + end if + print *, " " + + ! Check to see if the hybrid levelpool_output data structure exists + print *, "Checking pointer association on data%levelpool_output for hybrid reservoirs" + if ( associated(hybrid_data%state%levelpool_ptr%output) ) then + print *, "PASSED" + ptr_state(8) = .true. + else + print *, "FAILED" + ptr_state(8) = .false. + end if + print *, " " + + + ! Now check the data members of each substructure + if ( ptr_state(1) ) then + data_state(1) = test_persistence_levelpool_hybrid_state(hybrid_data%state) + end if + + if ( ptr_state(2) ) then + data_state(2) = test_persistence_levelpool_hybrid_properties(hybrid_data%properties) + end if + + if ( ptr_state(3) ) then + data_state(3) = test_input_persistence_levelpool_hybrid(hybrid_data%input) + end if + + if ( ptr_state(4) ) then + data_state(4) = test_output_persistence_levelpool_hybrid(hybrid_data%output) + end if + + ! Test Levelpool Substructures + if ( ptr_state(5) ) then + data_state(5) = test_hybrid_levelpool_state(hybrid_data%state) + end if + + if ( ptr_state(6) ) then + data_state(6) = test_hybrid_levelpool_properties(hybrid_data%state) + end if + + if ( ptr_state(7) ) then + data_state(7) = test_hybrid_levelpool_input(hybrid_data%state%levelpool_ptr%input) + end if + + if ( ptr_state(8) ) then + data_state(8) = test_hybrid_levelpool_output(hybrid_data%state%levelpool_ptr%output) + end if + + + if ( all(ptr_state) .and. all(data_state) ) then + print *, "========================================================================" + print *, "All Hybrid Object Tests Passed" + print *, "========================================================================" + rv = .true. + + else + print *, "========================================================================" + print *, "Not All Hybrid Object Tests Passed" + print *, "========================================================================" + + end if + + flush(6) + end function persistence_levelpool_hybrid_data_info + + + ! Test to see that each member of the input structure is correctly allocated and readable + function test_input_persistence_levelpool_hybrid(o) result(rv) + type (reservoir_input), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the input structure" + print *, " " + + print *, "Checking read on inflow" + print *, o%inflow + if ( o%inflow .lt. 0.0 - epsilon(0.0) .or. o%inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lateral_inflow" + print *, o%lateral_inflow + if ( o%lateral_inflow .lt. 0.0 - epsilon(0.0) .or. o%lateral_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on previous_timestep_inflow" + print *, o%previous_timestep_inflow + if ( o%previous_timestep_inflow .lt. 0.0 - epsilon(0.0) .or. o%previous_timestep_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_input_persistence_levelpool_hybrid + + ! Test to see that each member of the output structure is correctly allocated and readable + function test_output_persistence_levelpool_hybrid(o) result(rv) + type (reservoir_output), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the output structure" + print *, " " + + print *, "Checking read on outflow" + print *, o%outflow + if ( o%outflow .lt. 0.0 - epsilon(0.0) .or. o%outflow .gt. 0.0 + epsilon(0.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_output_persistence_levelpool_hybrid + + ! Test to see that each member of the state structure is correctly allocated and readable + function test_persistence_levelpool_hybrid_state(o) result(rv) + type (hybrid_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid state data structure" + print *, " " + + print *, "Checking read on water_elevation" + print *, o%water_elevation + if ( o%water_elevation .lt. 1333.10938 - epsilon(1333.109380) .or. o%water_elevation .gt. 1333.10938 + epsilon(1333.10938) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on current_storage" + print *, o%current_storage + if ( o%current_storage .lt. 3.90669926E+09 - epsilon(3.90669926E+09) .or. o%current_storage .gt. 3.90669926E+09 + epsilon(3.90669926E+09) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on gage_discharge" + print *, o%gage_discharge + if ( o%gage_discharge .lt. 0.0 - epsilon(0.0) .or. o%gage_discharge .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on persisted_outflow" + print *, o%persisted_outflow + if ( o%persisted_outflow .lt. 0.0 - epsilon(0.0) .or. o%persisted_outflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weight_update_time" + print *, o%weight_update_time + if ( o%weight_update_time .ne. 0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on timeslice_update_time" + print *, o%timeslice_update_time + if ( o%timeslice_update_time .ne. 0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on current_time" + print *, o%current_time + if ( o%current_time .ne. 0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on persistence_weight_index" + print *, o%persistence_weight_index + if ( o%persistence_weight_index .ne. 12) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on levelpool_current_weight" + print *, o%levelpool_current_weight + if ( o%levelpool_current_weight .lt. 0.0 - epsilon(0.0) .or. o%levelpool_current_weight .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on persistence_current_weight" + print *, o%persistence_current_weight + if ( o%persistence_current_weight .lt. 0.0 - epsilon(0.0) .or. o%persistence_current_weight .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_persistence_levelpool_hybrid_state + + + ! Test to see that each member of the properties structure is correctly allocated and readable + function test_persistence_levelpool_hybrid_properties(o) result(rv) + type (hybrid_properties_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid properties data structure" + print *, " " + + print *, "Checking read on min_storage" + print *, o%min_storage + if ( o%min_storage .lt. 0.0 - epsilon(0.0) .or. o%min_storage .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on max_storage" + print *, o%max_storage + if ( o%max_storage .lt. 4.34078003E+09 - epsilon(4.34078003E+09) .or. o%max_storage .gt. 4.34078003E+09 + epsilon(4.34078003E+09) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lake_area" + print *, o%lake_area + if ( o%lake_area .lt. 209632000.0 - epsilon(209632000.0) .or. o%lake_area .gt. 209632000.0 + epsilon(209632000.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_elevation" + print *, o%orifice_elevation + if ( o%orifice_elevation .lt. 1314.47339 - epsilon(1314.47339) .or. o%orifice_elevation .gt. 1314.47339 + epsilon(1314.47339) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lake_number" + print *, o%lake_number + if ( o%lake_number .ne. 402142) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on gage_id" + print *, o%gage_id + if ( ADJUSTL(trim(o%gage_id)) .ne. '07198000') then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on observation_lookback_hours" + print *, o%observation_lookback_hours + if ( o%observation_lookback_hours .ne. 12) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on observation_update_time_interval_seconds" + print *, o%observation_update_time_interval_seconds + if ( o%observation_update_time_interval_seconds .ne. 1000000000) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weight_update_time_interval" + print *, o%weight_update_time_interval + if ( o%weight_update_time_interval .ne. 86400) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on persistence_weighted_coefficients" + print *, o%persistence_weighted_coefficients + if ( o%persistence_weighted_coefficients(1) .lt. 1.0 - epsilon(1.0) .or. o%persistence_weighted_coefficients(1) .gt. 1.0 + epsilon(1.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_persistence_levelpool_hybrid_properties + + + ! Levelpool data tests + ! Test to see that each member of the levelpool input structure is correctly allocated and readable + function test_hybrid_levelpool_input(o) result(rv) + type (reservoir_input), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid levelpool input structure" + print *, " " + + print *, "Checking read on inflow" + print *, o%inflow + if ( o%inflow .lt. 0.0 - epsilon(0.0) .or. o%inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lateral_inflow" + print *, o%lateral_inflow + if ( o%lateral_inflow .lt. 0.0 - epsilon(0.0) .or. o%lateral_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on previous_timestep_inflow" + print *, o%previous_timestep_inflow + if ( o%previous_timestep_inflow .lt. 0.0 - epsilon(0.0) .or. o%previous_timestep_inflow .gt. 0.0 + epsilon(0.0) )then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_hybrid_levelpool_input + + ! Test to see that each member of the levelpool output structure is correctly allocated and readable + function test_hybrid_levelpool_output(o) result(rv) + type (reservoir_output), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid levelpool output structure" + print *, " " + + print *, "Checking read on outflow" + print *, o%outflow + if ( o%outflow .lt. 0.0 - epsilon(0.0) .or. o%outflow .gt. 0.0 + epsilon(0.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_hybrid_levelpool_output + + ! Test to see that each member of the levelpool state structure is correctly allocated and readable + function test_hybrid_levelpool_state(o) result(rv) + type (hybrid_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid levelpool state data structure" + print *, " " + + print *, "Checking read on water_elevation" + print *, o%levelpool_ptr%state%water_elevation + if ( o%levelpool_ptr%state%water_elevation .lt. 0.0 - epsilon(0.0) .or. o%levelpool_ptr%state%water_elevation .gt. 0.0 + epsilon(0.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_hybrid_levelpool_state + + + ! Test to see that each member of the levelpool properties structure is correctly allocated and readable + function test_hybrid_levelpool_properties(o) result(rv) + type (hybrid_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the hybrid levelpool properties data structure" + print *, " " + + print *, "Checking read on lake_area" + print *, o%levelpool_ptr%properties%lake_area + if ( o%levelpool_ptr%properties%lake_area .lt. 209.632004 - epsilon(209.632004) .or. o%levelpool_ptr%properties%lake_area .gt. 209.632004 + epsilon(209.632004) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_elevation" + print *, o%levelpool_ptr%properties%weir_elevation + if ( o%levelpool_ptr%properties%weir_elevation .lt. 1332.07410 - epsilon(1332.07410) .or. o%levelpool_ptr%properties%weir_elevation .gt. 1332.07410 + epsilon(1332.07410) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_coeffecient" + print *, o%levelpool_ptr%properties%weir_coeffecient + if ( o%levelpool_ptr%properties%weir_coeffecient .lt. 0.400000006 - epsilon(0.400000006) .or. o%levelpool_ptr%properties%weir_coeffecient .gt. 0.400000006 + epsilon(0.400000006) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_length" + print *, o%levelpool_ptr%properties%weir_length + if ( o%levelpool_ptr%properties%weir_length .lt. 10.0 - epsilon(10.0) .or. o%levelpool_ptr%properties%weir_length .gt. 10.0 + epsilon(10.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_elevation" + print *, o%levelpool_ptr%properties%orifice_elevation + if ( o%levelpool_ptr%properties%orifice_elevation .lt. 1314.47339 - epsilon(1314.47339) .or. o%levelpool_ptr%properties%orifice_elevation .gt. 1314.47339 + epsilon(1314.47339) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_coefficient" + print *, o%levelpool_ptr%properties%orifice_coefficient + if ( o%levelpool_ptr%properties%orifice_coefficient .lt. 0.100000001 - epsilon(0.100000001) .or. o%levelpool_ptr%properties%orifice_coefficient .gt. 0.100000001 + epsilon(0.100000001) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_area" + print *, o%levelpool_ptr%properties%orifice_area + if ( o%levelpool_ptr%properties%orifice_area .lt. 1.0 - epsilon(1.0) .or. o%levelpool_ptr%properties%orifice_area .gt. 1.0 + epsilon(1.0) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on max_depth" + print *, o%levelpool_ptr%properties%max_depth + if ( o%levelpool_ptr%properties%max_depth .lt. 1335.18005 - epsilon(1335.18005) .or. o%levelpool_ptr%properties%max_depth .gt. 1335.18005 + epsilon(1335.18005) ) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lake_number" + print *, o%levelpool_ptr%properties%lake_number + if ( o%levelpool_ptr%properties%lake_number .ne. 402142) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_hybrid_levelpool_properties + +end module diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/CMakeLists.txt b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/CMakeLists.txt new file mode 100644 index 000000000..64cd6e6c7 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 2.8) + +add_library(hydro_routing_reservoirs_rfc STATIC + module_rfc_forecasts.F + module_rfc_forecasts_state.F + module_rfc_forecasts_properties.F +) + +add_dependencies(hydro_routing_reservoirs_rfc hydro_routing_reservoirs) +add_dependencies(hydro_routing_reservoirs_rfc hydro_routing_reservoirs_levelpool) diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/Makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/Makefile new file mode 100644 index 000000000..9833d63ae --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/Makefile @@ -0,0 +1,28 @@ + +include ../macros + +MODFLAG := -I ../mod + +%.o : %.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) $< + +.PHONY: all mod test + +all: mod + +mod: + #Build each sub module then build the module that depends on all sub modules + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_rfc_forecasts_properties.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_rfc_forecasts_state.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_rfc_forecasts.F + $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_rfc_forecasts_tests.F + ar -r ../lib/libHYDRO.a module_rfc_forecasts_properties.o + ar -r ../lib/libHYDRO.a module_rfc_forecasts_state.o + ar -r ../lib/libHYDRO.a module_rfc_forecasts.o + ar -r ../lib/libHYDRO.a module_rfc_forecasts_tests.o + + cp *.mod ../mod + +clean: + rm -f *.o + rm -f *.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts.F new file mode 100644 index 000000000..a793ec79c --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts.F @@ -0,0 +1,387 @@ +! This module defines and instantiates objects +! for a RFC Forecasts type reservoir. The RFC +! Forecasts reservoir type inherits input and +! output types from the reservoir base module +! and calls instantiation of these into +! sub-objects. The RFC Forecasts reservoir +! type also points to types for RFC Forecasts +! properties and state and calls instantiation +! of these into sub-objects. There is also a +! subroutine to run RFC Forecasts reservoir +! that is derived from the reservoir base +! type interface to run reservoir. + +module module_rfc_forecasts + + use module_rfc_forecasts_properties, only: rfc_forecasts_properties_interface + use module_rfc_forecasts_state, only: rfc_forecasts_state_interface + use module_levelpool, only: levelpool + use module_reservoir_utilities, only: create_rfc_forecasts_diagnostic_log_file, & + log_rfc_forecasts_diagnostic_data + use module_reservoir, only: reservoir, reservoir_input, reservoir_output + use module_reservoir_read_rfc_time_series_data, only: time_series_data + use module_hydro_stop, only: HYDRO_stop + implicit none + + ! Extend/derive rfc forecasts type from the abstract base + ! type for reservoirs. + type, extends(reservoir) :: rfc_forecasts + + ! Define pointers to sub-types / sub-objects to and + ! held by an rfc reservoir object. + type (rfc_forecasts_properties_interface), pointer :: properties => null() + type (rfc_forecasts_state_interface), pointer :: state => null() + + logical :: pointer_allocation_guard = .false. + + contains + + procedure :: init => rfc_forecasts_init + procedure :: destroy => rfc_forecasts_destroy + procedure :: run => run_rfc_forecasts_reservoir + + end type rfc_forecasts + +#ifndef NCEP_WCOSS + integer, parameter :: log_warning = 6 +#else + integer, parameter :: log_warning = 78 +#endif + +contains + + ! RFC Forecasts Constructor + subroutine rfc_forecasts_init(this, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, & + lake_number, reservoir_type, reservoir_parameter_file, start_date, & + time_series_path, forecast_lookback_hours) + + implicit none + class(rfc_forecasts), intent(inout) :: this ! object being initialized + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + integer, intent(in) :: lake_number ! lake number + integer, intent(in) :: reservoir_type ! reservoir type + character(len=*), intent(in) :: reservoir_parameter_file + character(len=19), intent(in) :: start_date + character(len=*), intent(in) :: time_series_path + integer, intent(in) :: forecast_lookback_hours + integer :: update_offset_seconds + character(len=15) :: lake_number_string + character(len=15) :: reservoir_type_string + +#ifdef RESERVOIR_D + ! Create diagnostic log file only for development/debugging purposes + call create_rfc_forecasts_diagnostic_log_file(lake_number) +#endif + + if (this%pointer_allocation_guard .eqv. .false. ) then + + if (reservoir_type .ne. 4) then + ! Call hydro_stop if reservoir_type is not RFC Forecast. + write(lake_number_string, "(I15)") lake_number + write(reservoir_type_string, "(I15)") reservoir_type + call hydro_stop("ERROR: Incorrect reservoir type for reservoir " // trim(ADJUSTL(lake_number_string)) // & + ". Expected reservoir type 4 and instead received reservoir type " // reservoir_type_string // "." ) + end if + + ! try to allocate input + allocate ( this%input ) + if ( .not. associated(this%input) ) then + ! if the input structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate rfc forecasts input structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the input structure + call this%input%init() + end if + + ! try to allocate output + allocate ( this%output ) + if ( .not. associated(this%output) ) then + ! if the output structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate rfc forecasts output structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize the output structure + call this%output%init() + end if + + ! try to allocate properties + allocate ( this%properties ) + if ( .not. associated(this%properties) ) then + ! if the properties structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate rfc forecasts properties structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize rfc forecasts properties + call this%properties%init(lake_number, reservoir_parameter_file) + end if + this%pointer_allocation_guard = .true. + + ! try to allocate state + allocate ( this%state ) + if ( .not. associated(this%state) ) then + ! if the state structure could not be created, call hydro_stop. + write(lake_number_string, "(I15)") lake_number + call hydro_stop("ERROR: Failure to allocate rfc forecasts state structure for reservoir " & + // trim(ADJUSTL(lake_number_string)) // ".") + else + ! initialize rfc forecasts state + call this%state%init(water_elevation, lake_area, lake_max_water_elevation, & + orifice_elevation, initial_fractional_depth) + end if + this%pointer_allocation_guard = .true. + + end if + + ! Allocate a single level pool reservoir + allocate(levelpool :: this%state%levelpool_ptr) + + ! Initialize level pool reservoir + call this%state%levelpool_ptr%init(water_elevation, lake_area, & + weir_elevation, weir_coeffecient, weir_length, dam_length, orifice_elevation, & + orifice_coefficient, orifice_area, lake_max_water_elevation, lake_number) + + ! Call to initialize time series data object + call time_series_data%init(start_date, time_series_path, forecast_lookback_hours, & + this%properties%rfc_timeslice_offset_hours, this%properties%rfc_gage_id, & + this%properties%lake_number, this%properties%lookback_seconds, this%properties%total_counts, & + this%properties%observed_counts, this%properties%time_step_seconds, this%state%discharges, & + this%state%forecast_found, this%state%all_discharges_synthetic, this%properties%time_series_file_name) + + ! If the forecast was found and returned a discharge array + if (this%state%forecast_found) then + + ! Set the assimilated_source_file + this%state%assimilated_source_file = this%properties%time_series_file_name + + ! Use modulo to calculate the offset seconds in between the hourly time-step to set the update time + ! for shifting values in the discharge array. + update_offset_seconds = mod(this%properties%lookback_seconds, this%properties%time_step_seconds) + + this%state%time_series_update_time = this%properties%time_step_seconds - update_offset_seconds + + ! Set starting index in the time series discharge array depending on the lookback seconds to when + ! the RFC time series file was found, the number of observed values to offset from the beginning of + ! discharge array to reach the T0 issue time, and the offset hours that the model searched in the future + ! of model start time to for the time series file issue time. + this%state%time_series_index = this%properties%lookback_seconds / this%properties%time_step_seconds + 1 & + + this%properties%observed_counts - this%properties%rfc_timeslice_offset_hours + + ! Check if starting index is out of range of discharge array and set forecast_found to false if out of range. + if (this%state%time_series_index .le. 0 .or. this%state%time_series_index .ge. this%properties%total_counts) then + this%state%forecast_found = .false. + + this%state%time_series_index = 1 + end if + end if + + end subroutine rfc_forecasts_init + + ! RFC Forecasts Destructor + subroutine rfc_forecasts_destroy(this) + implicit none + class(rfc_forecasts), intent(inout) :: this ! object being destroyed + end subroutine rfc_forecasts_destroy + + ! Subroutine for running reservoir for a rfc forecasts reservoir + subroutine run_rfc_forecasts_reservoir(this, previous_timestep_inflow, inflow, & + lateral_inflow, water_elevation, outflow, routing_period, dynamic_reservoir_type, & + assimilated_value, assimilated_source_file) + implicit none + class(rfc_forecasts), intent(inout) :: this + real, intent(in) :: previous_timestep_inflow ! cubic meters per second (cms) + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer, intent(out):: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real, intent(out) :: assimilated_value ! value assimilated from observation or forecast + character(len=256), intent(out) :: assimilated_source_file ! source file of assimilated value + real :: levelpool_outflow ! cubic meters per second (cms) + integer :: missing_outflow_index + character(len=15) :: lake_number_string + + ! If first timestep, then check forecast aspects + if (this%state%current_time == 0) then + if (this%state%forecast_found) then + + ! If all of the values are synthetic or any of the values are less than 0.0 + ! or greater than or equal to the MS river historical peak, then + ! do not use the time series discharge array and revert to level pool. + ! Otherwise, if forecast was not found, also revert to level pool. + if (this%state%all_discharges_synthetic) then + this%state%forecast_found = .false. + + write(lake_number_string, "(I15)") this%properties%lake_number + write(log_warning,*) "WARNING: RFC Forecast Time Series discharges for reservoir ", & + trim(ADJUSTL(lake_number_string)), " using time series file ", & + trim(ADJUSTL(this%properties%time_series_file_name)), & + " are all synthetic. This reservoir will use level pool calculations instead." + + else if (minval(this%state%discharges) < 0.0) then + this%state%forecast_found = .false. + + write(lake_number_string, "(I15)") this%properties%lake_number + write(log_warning,*) "WARNING: RFC Forecast Time Series discharges for reservoir ", & + trim(ADJUSTL(lake_number_string)), " using time series file ", & + trim(ADJUSTL(this%properties%time_series_file_name)), & + " contains missing or negative values. This reservoir will use level pool calculations instead." + + !! peak flow on MS river *2 + !! http://nwis.waterdata.usgs.gov/nwis/peak?site_no=07374000&agency_cd=USGS&format=html + !! baton rouge 1945: 1,473,000cfs=41,711cms, multiply it roughly by 2 + else if (maxval(this%state%discharges) .ge. 90000.0) then + this%state%forecast_found = .false. + + write(lake_number_string, "(I15)") this%properties%lake_number + write(log_warning,*) "WARNING: RFC Forecast Time Series discharges for reservoir ", & + trim(ADJUSTL(lake_number_string)), " using time series file ", & + trim(ADJUSTL(this%properties%time_series_file_name)), & + " contain one or more values greater than or equal to 90,000 Cubic Meters per Second", & + " (twice the Mississippi River historical peak flow). These values are assumed to be incorrect.", & + " This reservoir will use level pool calculations instead." + end if + + else + write(lake_number_string, "(I15)") this%properties%lake_number + write(log_warning,*) "WARNING: RFC Forecast Time Series file for reservoir ", trim(ADJUSTL(lake_number_string)), & + " is not available. This reservoir will use level pool calculations instead." + end if + end if + + ! Update the current time + this%state%current_time = this%state%current_time + int(routing_period) + + ! Update input variables + this%input%inflow = inflow + this%input%lateral_inflow = lateral_inflow + + ! Update state water elevation + this%state%water_elevation = water_elevation + + ! Run levelpool reservoir + call this%state%levelpool_ptr%run(previous_timestep_inflow, inflow, & + lateral_inflow, this%state%water_elevation, levelpool_outflow, routing_period, this%state%levelpool_reservoir_type, & + this%state%levelpool_assimilated_value, this%state%levelpool_assimilated_source_file) + + ! Check if Routing Period is greater than 1 hour, and if true, set forecast_found to false. + if (routing_period .gt. 3600) then + this%state%forecast_found = .false. + + write(lake_number_string, "(I15)") this%properties%lake_number + write(log_warning,*) "WARNING: the routing period is greater than one hour. Therefore, the RFC forecasts", & + " cannot be used for reservoir ", trim(ADJUSTL(lake_number_string)), & + ". This reservoir will use level pool calculations instead." + end if + + ! If a forecast is found and the current time is less than the max time an RFC Forecast is persisted + if (this%state%forecast_found .and. this%state%current_time .le. this%properties%rfc_forecast_persist_seconds) then + + ! If current time is at or past the update time to cycle to the next value in the discharge array and the number + ! forecast counts has not yet been reached + if (this%state%current_time .ge. this%state%time_series_update_time .and. this%state%time_series_index & + .lt. this%properties%total_counts) then + + ! Increment time series index + this%state%time_series_index = this%state%time_series_index + 1 + + ! Set next update time + this%state%time_series_update_time = this%state%time_series_update_time + this%properties%time_step_seconds + + end if + + ! Set outflow to corresponding discharge from array + this%output%outflow = this%state%discharges(this%state%time_series_index) + + ! Set dynamic_reservoir_type to RFC Forecasts Type + this%state%dynamic_reservoir_type = this%properties%reservoir_type + + ! Set the assimilated_value to corresponding discharge from array + this%state%assimilated_value = this%state%discharges(this%state%time_series_index) + + ! Check for outflows less than 0 and cycle backwards in the array until a + ! non-negative value is found. If all previous values are negative, then + ! use level pool outflow. + if (this%output%outflow < 0) then + missing_outflow_index = this%state%time_series_index + + do while (this%output%outflow < 0 .and. missing_outflow_index > 1) + missing_outflow_index = missing_outflow_index - 1 + + this%output%outflow = this%state%discharges(missing_outflow_index) + end do + + if (this%output%outflow < 0) then + this%output%outflow = levelpool_outflow + + ! Set dynamic_reservoir_type to levelpool type + this%state%dynamic_reservoir_type = this%state%levelpool_reservoir_type + + ! Set the assimilated_value to sentinel, -9999.0 + this%state%assimilated_value = -9999.0 + + ! Set the assimilated_source_file to empty string + this%state%assimilated_source_file = "" + end if + + end if + + else + this%output%outflow = levelpool_outflow + + ! Set dynamic_reservoir_type to levelpool type + this%state%dynamic_reservoir_type = this%state%levelpool_reservoir_type + + ! Set the assimilated_value to sentinel, -9999.0 + this%state%assimilated_value = -9999.0 + + ! Set the assimilated_source_file to empty string + this%state%assimilated_source_file = "" + end if + + ! Update output variable returned from this subroutine + outflow = this%output%outflow + + ! Set current inflow to previous_timestep_inflow + this%input%previous_timestep_inflow = inflow + + ! Update water_elevation variable returned from this subroutine + water_elevation = this%state%water_elevation + + ! Update the dynamic_reservoir_type returned from this subroutine + dynamic_reservoir_type = this%state%dynamic_reservoir_type + + ! Update the assimilated_value returned from this subroutine + assimilated_value = this%state%assimilated_value + + ! Update the assimilated_source_file returned from this subroutine + assimilated_source_file = this%state%assimilated_source_file + +#ifdef RESERVOIR_D + ! Log diagnostic data only for development/debugging purposes + call log_rfc_forecasts_diagnostic_data(this%properties%lake_number, this%state%current_time, & + this%properties%time_step_seconds, this%state%time_series_update_time, this%properties%lookback_seconds, & + this%state%time_series_index, this%properties%rfc_gage_id, this%input%inflow, this%state%water_elevation, & + levelpool_outflow, this%output%outflow) +#endif + + end subroutine run_rfc_forecasts_reservoir + +end module module_rfc_forecasts diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_properties.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_properties.F new file mode 100644 index 000000000..9fcf67bd1 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_properties.F @@ -0,0 +1,96 @@ +! This module defines and instantiates objects +! for an rfc forecasts type reservoir's +! parameters/properties. Properties holds +! static/unchanging variables that are +! set when the given reservoir object is +! initialized/instantiated. + +module module_rfc_forecasts_properties + use module_reservoir_utilities, only: read_netcdf_lake_id, & + read_reservoir_parameters_netcdf_rfc_gage_id, & + read_reservoir_parameters_netcdf_rfc_integer, & + handle_err + use module_reservoir, only: reservoir_properties + use netcdf + + implicit none + + ! Extend/derive rfc forecasts properties from the abstract base + ! type for reservoir properties. + type, extends(reservoir_properties) :: rfc_forecasts_properties_interface + integer :: lake_number ! lake number + integer :: reservoir_type ! reservoir type + character(len=5) :: rfc_gage_id + integer :: lookback_seconds + integer :: total_counts + integer :: observed_counts + integer :: time_step_seconds + integer :: rfc_forecast_persist_days + integer :: rfc_forecast_persist_seconds + integer :: rfc_timeslice_offset_hours + integer :: observation_hours + character(len=256):: time_series_file_name + + contains + + procedure :: init => rfc_forecasts_properties_init + procedure :: destroy => rfc_forecasts_properties_destroy + + end type rfc_forecasts_properties_interface + + integer, parameter :: seconds_in_day = 86400 + integer, parameter :: observation_hours = 48 + +contains + + ! RFC Forecasts Properties Constructor + subroutine rfc_forecasts_properties_init(this, lake_number, reservoir_parameter_file) + implicit none + class(rfc_forecasts_properties_interface), intent(inout) :: this ! the type object being initialized + integer, intent(in) :: lake_number ! lake number + character(len=*), intent(in) :: reservoir_parameter_file + integer :: ncid, var_id, lake_id_index + integer :: status ! status of reading NetCDF + + this%lake_number = lake_number + + ! reservoir_type set to 4 for RFC type forecasts + this%reservoir_type = 4 + + this%observation_hours = observation_hours + + this%time_series_file_name = "" + + ! Open Reservoir Parameter NetCDF file + status = nf90_open(path = reservoir_parameter_file, mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Read relevant properties from Persistence Parameter NetCDF + call read_netcdf_lake_id(ncid, lake_number, "rfc_lake_id", reservoir_parameter_file, lake_id_index) + + call read_reservoir_parameters_netcdf_rfc_gage_id(ncid, lake_id_index, "rfc_gage_id", & + reservoir_parameter_file, this%rfc_gage_id) + + call read_reservoir_parameters_netcdf_rfc_integer(ncid, lake_id_index, "rfc_forecast_persist", & + reservoir_parameter_file, this%rfc_forecast_persist_days) + + this%rfc_forecast_persist_seconds = this%rfc_forecast_persist_days * seconds_in_day + + call read_reservoir_parameters_netcdf_rfc_integer(ncid, lake_id_index, "rfc_timeslice_offset", & + reservoir_parameter_file, this%rfc_timeslice_offset_hours) + + ! Close Reservoir Parameter NetCDF file + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close reservoir parameter file" & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + end subroutine rfc_forecasts_properties_init + + ! RFC Forecasts Properties Destructor + subroutine rfc_forecasts_properties_destroy(this) + implicit none + class(rfc_forecasts_properties_interface), intent(inout) :: this ! the type object being destroyed + end subroutine rfc_forecasts_properties_destroy + +end module module_rfc_forecasts_properties diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_state.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_state.F new file mode 100644 index 000000000..bacb1829f --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_state.F @@ -0,0 +1,88 @@ +! This module defines and instantiates objects +! for an rfc forecasts type reservoir's state. +! State holds and tracks dynamic/changing variables +! that are only relevant to the given rfc forecasts +! reservoir object and not other modules or areas +! of the system. + +module module_rfc_forecasts_state + + use module_levelpool, only: levelpool + use module_reservoir, only: reservoir_state + implicit none + + ! Extend/derive rfc forecasts state from the abstract base + ! type for reservoir state. + type, extends(reservoir_state) :: rfc_forecasts_state_interface + real :: water_elevation ! meters AMSL + real, allocatable, dimension(:) :: discharges + logical, allocatable, dimension(:) :: synthetic_values + integer :: time_series_update_time ! seconds + integer :: current_time ! seconds + integer :: time_series_index + logical :: forecast_found + logical :: all_discharges_synthetic + integer :: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real :: assimilated_value + character(len=256) :: assimilated_source_file + integer :: levelpool_reservoir_type ! reservoir type for levelpool + real :: levelpool_assimilated_value ! levelpool assimilated sentinel value + character(len=256) :: levelpool_assimilated_source_file ! empty string for levelpool assimilated source + + type (levelpool), pointer :: levelpool_ptr ! pointer to levelpool object + + contains + + procedure :: init => rfc_forecasts_state_init + procedure :: destroy => rfc_forecasts_state_destroy + + end type rfc_forecasts_state_interface + +contains + + ! RFC Forecasts State Constructor + subroutine rfc_forecasts_state_init(this, water_elevation, lake_area, lake_max_water_elevation, orifice_elevation, initial_fractional_depth) + implicit none + class(rfc_forecasts_state_interface), intent(inout) :: this ! the type object being initialized + real, intent(in) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + + ! Initialize the state water elevation in same manner as in module_RT.F + this%water_elevation = orifice_elevation + ((lake_max_water_elevation - orifice_elevation) * initial_fractional_depth) + this%time_series_update_time = 0 + this%current_time = 0 + this%time_series_index = 1 + this%forecast_found = .FALSE. + this%all_discharges_synthetic = .FALSE. + + ! Initialize dynamic_reservoir_type to 4 for RFC type reservoir + this%dynamic_reservoir_type = 4 + + ! Initialize to default sentinel, -9999.0 + this%assimilated_value = -9999.0 + + ! Initialize to default empty string + this%assimilated_source_file = "" + + ! Levelpool reservoir type set to 1 + this%levelpool_reservoir_type = 1 + + ! Set to default sentinel, -999.0 + this%levelpool_assimilated_value = -9999.0 + + ! Set to default string + this%levelpool_assimilated_source_file = "" + + end subroutine rfc_forecasts_state_init + + ! RFC Forecasts State Destructor + subroutine rfc_forecasts_state_destroy(this) + implicit none + class(rfc_forecasts_state_interface), intent(inout) :: this ! the type object being destroyed + + end subroutine rfc_forecasts_state_destroy + +end module module_rfc_forecasts_state diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_tests.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_tests.F new file mode 100644 index 000000000..ccae41c91 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/RFC_Forecasts/module_rfc_forecasts_tests.F @@ -0,0 +1,435 @@ +! This module holds tests for initialization of +! various attributes of an RFC forecasts reservoir. +module module_rfc_forecasts_tests + use module_rfc_forecasts + use module_reservoir_read_rfc_time_series_data + +contains + + ! Testing + function rfc_forecasts_data_info(rfc_forecasts_reservoir_data) result(data_info_result) + implicit none + type (rfc_forecasts) :: rfc_forecasts_reservoir_data + logical :: data_info_result + logical, dimension(8) :: ptr_state + logical, dimension(8) :: data_state + + data_info_result = .false. + + ! Check to see if the rfc_forecasts_state data structure exists + print *, "Checking pointer association on data%state for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%state) ) then + print *, "PASSED" + ptr_state(1) = .true. + else + print *, "FAILED" + ptr_state(1) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts_properties data structure exists + print *, "Checking pointer association on data%properties for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%properties) ) then + print *, "PASSED" + ptr_state(2) = .true. + else + print *, "FAILED" + ptr_state(2) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts_input data structure exists + print *, "Checking pointer association on data%input for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%input) ) then + print *, "PASSED" + ptr_state(3) = .true. + else + print *, "FAILED" + ptr_state(3) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts_output data structure exists + print *, "Checking pointer association on data%output for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%output) ) then + print *, "PASSED" + ptr_state(4) = .true. + else + print *, "FAILED" + ptr_state(4) = .false. + end if + print *, " " + + ! Test levelpool structure + ! Check to see if the rfc_forecasts levelpool_state data structure exists + print *, "Checking pointer association on data%levelpool_state for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%state%levelpool_ptr%state) ) then + print *, "PASSED" + ptr_state(5) = .true. + else + print *, "FAILED" + ptr_state(5) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts levelpool_properties data structure exists + print *, "Checking pointer association on data%levelpool_properties for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%state%levelpool_ptr%properties) ) then + print *, "PASSED" + ptr_state(6) = .true. + else + print *, "FAILED" + ptr_state(6) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts levelpool_input data structure exists + print *, "Checking pointer association on data%levelpool_input for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%state%levelpool_ptr%input) ) then + print *, "PASSED" + ptr_state(7) = .true. + else + print *, "FAILED" + ptr_state(7) = .false. + end if + print *, " " + + ! Check to see if the rfc_forecasts levelpool_output data structure exists + print *, "Checking pointer association on data%levelpool_output for rfc forecasts reservoirs" + if ( associated(rfc_forecasts_reservoir_data%state%levelpool_ptr%output) ) then + print *, "PASSED" + ptr_state(8) = .true. + else + print *, "FAILED" + ptr_state(8) = .false. + end if + print *, " " + + + ! Now check the data members of each substructure + if ( ptr_state(1) ) then + data_state(1) = test_rfc_forecasts_state(rfc_forecasts_reservoir_data%state) + end if + + if ( ptr_state(2) ) then + data_state(2) = test_rfc_forecasts_properties(rfc_forecasts_reservoir_data%properties) + end if + + if ( ptr_state(3) ) then + data_state(3) = test_input_rfc_forecasts(rfc_forecasts_reservoir_data%input) + end if + + if ( ptr_state(4) ) then + data_state(4) = test_output_rfc_forecasts(rfc_forecasts_reservoir_data%output) + end if + + ! Test Levelpool Substructures + if ( ptr_state(5) ) then + data_state(5) = test_rfc_forecasts_levelpool_state(rfc_forecasts_reservoir_data%state) + end if + + if ( ptr_state(6) ) then + data_state(6) = test_rfc_forecasts_levelpool_properties(rfc_forecasts_reservoir_data%state) + end if + + if ( ptr_state(7) ) then + data_state(7) = test_rfc_forecasts_levelpool_input(rfc_forecasts_reservoir_data%state%levelpool_ptr%input) + end if + + if ( ptr_state(8) ) then + data_state(8) = test_rfc_forecasts_levelpool_output(rfc_forecasts_reservoir_data%state%levelpool_ptr%output) + end if + + if ( all(ptr_state) .and. all(data_state) ) then + data_info_result = .true. + print *, "========================================================================" + print *, "All RFC Forecast Reservoir Object Tests Passed" + print *, "========================================================================" + + else + data_info_result = .false. + print *, "========================================================================" + print *, "Not All RFC Forecast Reservoir Object Tests Passed" + print *, "========================================================================" + + end if + + end function rfc_forecasts_data_info + + ! Test to see that each member of the input structure is correctly allocated and readable + function test_input_rfc_forecasts(o) result(rv) + type (reservoir_input), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the input structure" + print *, " " + + print *, "Checking read on inflow" + print *, o%inflow + if ( o%inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lateral_inflow" + print *, o%lateral_inflow + if ( o%lateral_inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on previous_timestep_inflow" + print *, o%previous_timestep_inflow + if ( o%previous_timestep_inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_input_rfc_forecasts + + ! Test to see that each member of the output structure is correctly allocated and readable + function test_output_rfc_forecasts(o) result(rv) + type (reservoir_output), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the output structure" + print *, " " + + print *, "Checking read on outflow" + print *, o%outflow + if ( o%outflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_output_rfc_forecasts + + ! Test to see that each member of the state structure is correctly allocated and readable + function test_rfc_forecasts_state(o) result(rv) + type (rfc_forecasts_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc_forecasts state data structure" + print *, " " + + print *, "Checking read on water_elevation" + print *, o%water_elevation + if ( o%water_elevation .ne. 17.3999996) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_state + + + ! Test to see that each member of the properties structure is correctly allocated and readable + function test_rfc_forecasts_properties(o) result(rv) + type (rfc_forecasts_properties_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc_forecasts properties data structure" + print *, " " + + print *, "Checking read on lake_number" + print *, o%lake_number + if ( o%lake_number .ne. 3745478) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on rfc_gage_id" + print *, o%rfc_gage_id + if ( o%rfc_gage_id .ne. "DIEA4") then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_properties + + + ! Levelpool data tests + ! Test to see that each member of the levelpool input structure is correctly allocated and readable + function test_rfc_forecasts_levelpool_input(o) result(rv) + type (reservoir_input), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc forecasts levelpool input structure" + print *, " " + + print *, "Checking read on inflow" + print *, o%inflow + if ( o%inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lateral_inflow" + print *, o%lateral_inflow + if ( o%lateral_inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on previous_timestep_inflow" + print *, o%previous_timestep_inflow + if ( o%previous_timestep_inflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_levelpool_input + + ! Test to see that each member of the levelpool output structure is correctly allocated and readable + function test_rfc_forecasts_levelpool_output(o) result(rv) + type (reservoir_output), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc forecasts levelpool output structure" + print *, " " + + print *, "Checking read on outflow" + print *, o%outflow + if ( o%outflow .ne. 0.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_levelpool_output + + ! Test to see that each member of the levelpool state structure is correctly allocated and readable + function test_rfc_forecasts_levelpool_state(o) result(rv) + type (rfc_forecasts_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc forecasts levelpool state data structure" + print *, " " + + print *, "Checking read on water_elevation" + print *, o%levelpool_ptr%state%water_elevation + if ( o%levelpool_ptr%state%water_elevation .ne. 2.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_levelpool_state + + + ! Test to see that each member of the levelpool properties structure is correctly allocated and readable + function test_rfc_forecasts_levelpool_properties(o) result(rv) + type (rfc_forecasts_state_interface), intent(in) :: o + logical rv + + rv = .true. + + print *, "========================================================================" + print *, "Checking the values of the rfc forecasts levelpool properties data structure" + print *, " " + + print *, "Checking read on lake_area" + print *, o%levelpool_ptr%properties%lake_area + if ( o%levelpool_ptr%properties%lake_area .ne. 4.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_elevation" + print *, o%levelpool_ptr%properties%weir_elevation + if ( o%levelpool_ptr%properties%weir_elevation .ne. 6.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_coeffecient" + print *, o%levelpool_ptr%properties%weir_coeffecient + if ( o%levelpool_ptr%properties%weir_coeffecient .ne. 8.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on weir_length" + print *, o%levelpool_ptr%properties%weir_length + if ( o%levelpool_ptr%properties%weir_length .ne. 10.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_elevation" + print *, o%levelpool_ptr%properties%orifice_elevation + if ( o%levelpool_ptr%properties%orifice_elevation .ne. 12.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_coefficient" + print *, o%levelpool_ptr%properties%orifice_coefficient + if ( o%levelpool_ptr%properties%orifice_coefficient .ne. 14.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on orifice_area" + print *, o%levelpool_ptr%properties%orifice_area + if ( o%levelpool_ptr%properties%orifice_area .ne. 16.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on max_depth" + print *, o%levelpool_ptr%properties%max_depth + if ( o%levelpool_ptr%properties%max_depth .ne. 18.0) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + print *, "Checking read on lake_number" + print *, o%levelpool_ptr%properties%lake_number + if ( o%levelpool_ptr%properties%lake_number .ne. 3745478) then + rv = .false. + print *, "FAILED" + end if + print *, " " + + end function test_rfc_forecasts_levelpool_properties + +end module diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 new file mode 100644 index 000000000..5a7e25338 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 @@ -0,0 +1,154 @@ +module reservoir_to_c_hybrid + use module_persistence_levelpool_hybrid + + implicit none + +contains + + FUNCTION get_hybrid_handle() RESULT(handle) BIND(C, NAME='get_hybrid_handle') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_LOC + TYPE(C_PTR) :: handle + TYPE(persistence_levelpool_hybrid), POINTER :: hybrid_ptr ! ptr to hybrid object + ALLOCATE(hybrid_ptr) ! Allocate hybrid example object + handle = C_LOC(hybrid_ptr) ! Get the C address of the hybrid object and assign to handle + END FUNCTION get_hybrid_handle + + SUBROUTINE free_hybrid(handle) BIND(C, NAME='free_hybrid') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER + TYPE(C_PTR), INTENT(IN), VALUE :: handle + !TYPE(LP_Example), POINTER :: lp_ptr + TYPE(persistence_levelpool_hybrid), POINTER :: hybrid_ptr + CALL C_F_POINTER(handle, hybrid_ptr) + !DEALLOCATE(hybrid_ptr) + END SUBROUTINE free_hybrid + + SUBROUTINE init_hybrid(handle, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, lake_number, reservoir_type, reservoir_parameter_file, start_date, & + usgs_timeslice_path, usace_timeslice_path, observation_lookback_hours, & + observation_update_time_interval_seconds) BIND(C, NAME='init_hybrid') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, c_null_char + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + integer, intent(in) :: lake_number ! lake number + integer, intent(in) :: reservoir_type ! reservoir type + character(kind=c_char), dimension(*), intent(IN) :: reservoir_parameter_file + character(kind=c_char), dimension(*), intent(IN) :: start_date + character(kind=c_char), dimension(*), intent(IN) :: usgs_timeslice_path + character(kind=c_char), dimension(*), intent(IN) :: usace_timeslice_path + integer, intent(in) :: observation_lookback_hours + integer, intent(in) :: observation_update_time_interval_seconds + + type (persistence_levelpool_hybrid), POINTER :: hybrid_ptr ! ptr to hybrid object + + character(len=256) :: reservoir_parameter_file_F + character(len=256) :: start_date_F + character(len=256) :: usgs_timeslice_path_F + character(len=256) :: usace_timeslice_path_F + + integer :: char_index, nchars_reservoir_parameter_file, nchars_start_date, nchars_usgs_timeslice_path, nchars_usace_timeslice_path + + ! Use fortran calls to go find certain things like null_char instead of using this loop every time + ! Look up more about converting char array to string + + do char_index = 1, 256 + if (reservoir_parameter_file(char_index) == c_null_char) then + exit + else + reservoir_parameter_file_F(char_index:char_index) = reservoir_parameter_file(char_index) + end if + end do + nchars_reservoir_parameter_file = char_index - 1 ! Exclude null character from Fortran string + + do char_index = 1, 256 + if (start_date(char_index) == c_null_char) then + exit + else + start_date_F(char_index:char_index) = start_date(char_index) + end if + end do + nchars_start_date = char_index - 1 ! Exclude null character from Fortran string + + do char_index = 1, 256 + if (usgs_timeslice_path(char_index) == c_null_char) then + exit + else + usgs_timeslice_path_F(char_index:char_index) = usgs_timeslice_path(char_index) + end if + end do + nchars_usgs_timeslice_path = char_index - 1 ! Exclude null character from Fortran string + + do char_index = 1, 256 + if (usace_timeslice_path(char_index) == c_null_char) then + exit + else + usace_timeslice_path_F(char_index:char_index) = usace_timeslice_path(char_index) + end if + end do + nchars_usace_timeslice_path = char_index - 1 ! Exclude null character from Fortran string + + CALL C_F_POINTER(handle, hybrid_ptr) + + call hybrid_ptr%init(water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, lake_number, & + reservoir_type, reservoir_parameter_file_F(:nchars_reservoir_parameter_file), & + start_date_F(:nchars_start_date), usgs_timeslice_path_F(:nchars_usgs_timeslice_path), & + usace_timeslice_path_F(:nchars_usace_timeslice_path), observation_lookback_hours, & + observation_update_time_interval_seconds) + + END SUBROUTINE init_hybrid + + + SUBROUTINE run_hybrid(handle, inflow, lateral_inflow, water_elevation, outflow, routing_period) BIND(C, NAME='run_hybrid') + + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, C_LOC, C_NULL_CHAR + + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer :: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real :: assimilated_value ! value assimilated from observation or forecast + character(len=256) :: assimilated_source_file ! source file of assimilated value + + type (persistence_levelpool_hybrid), POINTER :: hybrid_ptr ! ptr to hybrid object + + CALL C_F_POINTER(handle, hybrid_ptr) + + call hybrid_ptr%run(inflow, & + inflow, & + lateral_inflow, & + water_elevation, & + outflow, & + routing_period, & + dynamic_reservoir_type, & + assimilated_value, & + assimilated_source_file) + + !Use something like below if needing to return assimilated_source_file + !copy the tmp str to c_str and null terminate + !assimilated_source_file = trim(str) // c_null_char + !deallocate tmp str + !deallocate(str) + !character(kind = C_CHAR), intent(out) :: assimilated_source_file(256) + !character(256), pointer :: str + !allocate(str) !allocate the pointer to hold the tmp string + + END SUBROUTINE run_hybrid + +end module reservoir_to_c_hybrid diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 new file mode 100644 index 000000000..8940e7924 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 @@ -0,0 +1,89 @@ +module reservoir_to_c + use module_levelpool + implicit none + +contains + + FUNCTION get_lp_handle() RESULT(handle) BIND(C, NAME='get_lp_handle') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_LOC + TYPE(C_PTR) :: handle + TYPE(levelpool), POINTER :: lp_ptr ! ptr to LP object + ALLOCATE(lp_ptr) ! Allocate LP example object + handle = C_LOC(lp_ptr) ! Get the C address of the LP object and assign to handle + END FUNCTION get_lp_handle + + SUBROUTINE free_lp(handle) BIND(C, NAME='free_lp') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER + TYPE(C_PTR), INTENT(IN), VALUE :: handle + TYPE(levelpool), POINTER :: lp_ptr + CALL C_F_POINTER(handle, lp_ptr) + DEALLOCATE(lp_ptr) + END SUBROUTINE free_lp + + SUBROUTINE init_lp(handle, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, max_depth, lake_number) BIND(C, NAME='init_lp') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: max_depth ! max depth of reservoir before overtop (meters) + integer, intent(in) :: lake_number ! lake number + type (levelpool), POINTER :: levelpool_ptr ! ptr to LP object + CALL C_F_POINTER(handle, levelpool_ptr) + + call levelpool_ptr%init(water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, max_depth, lake_number) + END SUBROUTINE init_lp + + SUBROUTINE run_lp(handle, inflow, lateral_inflow, water_elevation, outflow, routing_period) BIND(C, NAME='run_lp') + + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, C_LOC, C_NULL_CHAR + + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + + integer :: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real :: assimilated_value ! value assimilated from observation or forecast + character(len=256) :: assimilated_source_file ! source file of assimilated value + + type (levelpool), POINTER :: levelpool_ptr ! ptr to LP object + + CALL C_F_POINTER(handle, levelpool_ptr) + + call levelpool_ptr%run(inflow, & + inflow, & + lateral_inflow, & + water_elevation, & + outflow, & + routing_period, & + dynamic_reservoir_type, & + assimilated_value, & + assimilated_source_file) + + !Use something like below if needing to return assimilated_source_file + !copy the tmp str to c_str and null terminate + !assimilated_source_file = trim(str) // c_null_char + !deallocate tmp str + !deallocate(str) + !character(kind = C_CHAR), intent(out) :: assimilated_source_file(256) + !character(256), pointer :: str + !allocate(str) !allocate the pointer to hold the tmp string + + END SUBROUTINE run_lp + +end module reservoir_to_c diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 new file mode 100644 index 000000000..051d0abe7 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 @@ -0,0 +1,140 @@ +module reservoir_to_c_rfc + use module_rfc_forecasts + + implicit none + +contains + + FUNCTION get_rfc_handle() RESULT(handle) BIND(C, NAME='get_rfc_handle') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_LOC + TYPE(C_PTR) :: handle + TYPE(rfc_forecasts), POINTER :: rfc_ptr ! ptr to rfc object + ALLOCATE(rfc_ptr) ! Allocate rfc example object + handle = C_LOC(rfc_ptr) ! Get the C address of the rfc object and assign to handle + END FUNCTION get_rfc_handle + + SUBROUTINE free_rfc(handle) BIND(C, NAME='free_rfc') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER + TYPE(C_PTR), INTENT(IN), VALUE :: handle + !TYPE(LP_Example), POINTER :: lp_ptr + TYPE(rfc_forecasts), POINTER :: rfc_ptr + CALL C_F_POINTER(handle, rfc_ptr) + !DEALLOCATE(rfc_ptr) + END SUBROUTINE free_rfc + + SUBROUTINE init_rfc(handle, water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, lake_number, reservoir_type, reservoir_parameter_file, start_date, & + time_series_path, forecast_lookback_hours) BIND(C, NAME='init_rfc') + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, c_null_char + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) + real, intent(in) :: weir_coeffecient ! weir coefficient + real, intent(in) :: weir_length ! weir length (meters) + real, intent(in) :: dam_length ! dam length (meters) + real, intent(in) :: orifice_elevation ! orifice elevation (meters AMSL) + real, intent(in) :: orifice_coefficient ! orifice coefficient + real, intent(in) :: orifice_area ! orifice area (meters^2) + real, intent(in) :: lake_max_water_elevation ! max water elevation (meters) + real, intent(in) :: initial_fractional_depth ! initial fraction water depth + integer, intent(in) :: lake_number ! lake number + integer, intent(in) :: reservoir_type ! reservoir type + character(kind=c_char), dimension(*), intent(IN) :: reservoir_parameter_file + character(kind=c_char), dimension(*), intent(IN) :: start_date + character(kind=c_char), dimension(*), intent(IN) :: time_series_path + integer, intent(in) :: forecast_lookback_hours + + type (rfc_forecasts), POINTER :: rfc_ptr ! ptr to rfc object + + character(len=256) :: reservoir_parameter_file_F + character(len=256) :: start_date_F + character(len=256) :: time_series_path_F + + integer :: char_index, nchars_reservoir_parameter_file, nchars_start_date, nchars_time_series_path + + ! Use fortran calls to go find certain things like null_char instead of using this loop every time + ! Look up more about converting char array to string + + do char_index = 1, 256 + if (reservoir_parameter_file(char_index) == c_null_char) then + exit + else + reservoir_parameter_file_F(char_index:char_index) = reservoir_parameter_file(char_index) + end if + end do + nchars_reservoir_parameter_file = char_index - 1 ! Exclude null character from Fortran string + + do char_index = 1, 256 + if (start_date(char_index) == c_null_char) then + exit + else + start_date_F(char_index:char_index) = start_date(char_index) + end if + end do + nchars_start_date = char_index - 1 ! Exclude null character from Fortran string + + do char_index = 1, 256 + if (time_series_path(char_index) == c_null_char) then + exit + else + time_series_path_F(char_index:char_index) = time_series_path(char_index) + end if + end do + nchars_time_series_path = char_index - 1 ! Exclude null character from Fortran string + + CALL C_F_POINTER(handle, rfc_ptr) + + call rfc_ptr%init(water_elevation, & + lake_area, weir_elevation, weir_coeffecient, & + weir_length, dam_length, orifice_elevation, orifice_coefficient, & + orifice_area, lake_max_water_elevation, initial_fractional_depth, lake_number, & + reservoir_type, reservoir_parameter_file_F(:nchars_reservoir_parameter_file), & + start_date_F(:nchars_start_date), time_series_path_F(:nchars_time_series_path), & + forecast_lookback_hours) + + END SUBROUTINE init_rfc + + + SUBROUTINE run_rfc(handle, inflow, lateral_inflow, water_elevation, outflow, routing_period) BIND(C, NAME='run_rfc') + + USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, C_LOC, C_NULL_CHAR + + TYPE(C_PTR), INTENT(IN), VALUE :: handle + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer :: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real :: assimilated_value ! value assimilated from observation or forecast + character(len=256) :: assimilated_source_file ! source file of assimilated value + + type (rfc_forecasts), POINTER :: rfc_ptr ! ptr to rfc object + + CALL C_F_POINTER(handle, rfc_ptr) + + call rfc_ptr%run(inflow, & + inflow, & + lateral_inflow, & + water_elevation, & + outflow, & + routing_period, & + dynamic_reservoir_type, & + assimilated_value, & + assimilated_source_file) + + !Use something like below if needing to return assimilated_source_file + !copy the tmp str to c_str and null terminate + !assimilated_source_file = trim(str) // c_null_char + !deallocate tmp str + !deallocate(str) + !character(kind = C_CHAR), intent(out) :: assimilated_source_file(256) + !character(256), pointer :: str + !allocate(str) !allocate the pointer to hold the tmp string + + END SUBROUTINE run_rfc + +end module reservoir_to_c_rfc diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile new file mode 100644 index 000000000..9390b1216 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile @@ -0,0 +1,53 @@ +NETCDFINC = $(NETCDF_INC) +NETCDFLIB = -L$(NETCDF_LIB) -lnetcdff -lnetcdf +COMPILER90 = gfortran +F90FLAGS = -w -c -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4 +F90FLAGS := -g $(F90FLAGS) -I$(NETCDFINC) -static-libgfortran -static-libgcc -nodefaultlibs -lgfortran -lgcc -fPIC +AR = ar +ARFLAGS = rc + +VPATH = ./Level_Pool ./Persistence_Level_Pool_Hybrid ./RFC_Forecasts + +.PHONY: all + +all: test_main + +test_main: test_main.o binding.a bind_hybrid.a bind_rfc.a + gfortran -g -o $@ $^ + +test_main.o: test_binding.c + gcc -g -c -o $@ $< + +module_levelpool.o: module_reservoir.o module_levelpool_properties.o module_levelpool_state.o + +module_persistence_levelpool_hybrid.o: module_reservoir.o module_hydro_stop.o module_reservoir_utilities.o module_persistence_levelpool_hybrid_properties.o module_persistence_levelpool_hybrid_state.o module_levelpool.o module_reservoir_read_timeslice_data.o + +module_rfc_forecasts.o: module_reservoir.o module_hydro_stop.o module_reservoir_utilities.o module_rfc_forecasts_properties.o module_rfc_forecasts_state.o module_levelpool.o module_reservoir_read_rfc_time_series_data.o + +bind_lp.o: module_levelpool.o + +bind_hybrid.o: module_persistence_levelpool_hybrid.o + +bind_rfc.o: module_rfc_forecasts.o + +binding.a: module_reservoir.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o bind_lp.o + $(AR) $(ARFLAGS) $@ $^ + +bind_hybrid.a: module_reservoir.o module_hydro_stop.o module_reservoir_utilities.o module_persistence_levelpool_hybrid_properties.o module_persistence_levelpool_hybrid_state.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o module_reservoir_read_timeslice_data.o module_persistence_levelpool_hybrid.o bind_hybrid.o + $(AR) $(ARFLAGS) $@ $^ + +bind_rfc.a: module_reservoir.o module_hydro_stop.o module_reservoir_utilities.o module_rfc_forecasts_properties.o module_rfc_forecasts_state.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o module_reservoir_read_rfc_time_series_data.o module_rfc_forecasts.o bind_rfc.o + $(AR) $(ARFLAGS) $@ $^ + +%.o: %.F + $(COMPILER90) $(F90FLAGS) $(NETCDFLIB) -c -o $@ $< + +%.o: %.f90 + $(COMPILER90) $(F90FLAGS) $(NETCDFLIB) -c -o $@ $< + + +clean: + rm -f *.o + rm -f *.mod + rm -f *.a + rm -f reservoir_tests diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_hydro_stop.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_hydro_stop.F new file mode 100644 index 000000000..09bbdf62d --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_hydro_stop.F @@ -0,0 +1,48 @@ +module module_hydro_stop + +implicit none + +contains + +! stop the job due to the fatal error. + subroutine HYDRO_stop(msg) +!David Mattern commenting 3 below lines for reservoirs in t-route +!#ifdef MPP_LAND +! use module_mpp_land +!#endif + character(len=*) :: msg + integer :: ierr + ierr = 1 +#ifndef NCEP_WCOSS +!#ifdef HYDRO_D !! PLEASE NEVER UNCOMMENT THIS IFDEF, it's just one incredibly useful string. + write(6,*) "The job is stopped due to the fatal error. ", trim(msg) + call flush(6) +!#endif +#else + write(*,*) "FATAL ERROR: ", trim(msg) + write(78,*) "FATAL ERROR: ", trim(msg) + call flush(78) + close(78) +#endif +#ifdef MPP_LAND +#ifndef HYDRO_D + print*, "---" + print*, "FATAL ERROR! Program stopped. Recompile with environment variable HYDRO_D set to 1 for enhanced debug information." + print*, "" +#endif + +! call mpp_land_sync() +! write(my_id+90,*) msg +! call flush(my_id+90) + +!David Mattern commenting 2 below lines for reservoirs in t-route + !call mpp_land_abort() + !call MPI_finalize(ierr) +#else + stop "FATAL ERROR: Program stopped. Recompile with environment variable HYDRO_D set to 1 for enhanced debug information." +#endif + + return + end subroutine HYDRO_stop + +end module diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir.F new file mode 100644 index 000000000..d48d3347c --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir.F @@ -0,0 +1,150 @@ +! This module is the base module for reservoirs that defines +! abstract types for a reservoir base, reservoir state, and +! reservoir properties. It also defines types for the inputs +! and outputs common to all sub-types of reservoirs. Finally, +! it defines a pointer to a reservoir object and an interface +! for running a reservoir. +module module_reservoir + + ! Defines the reservoir base state type that sub-types of + ! reservoirs will derive from. Since this is abstract, an instance + ! of reservoir state cannot be created but only derived to sub-types + ! of reservoirs. State holds and tracks dynamic/changing variables + ! that are only relevant to the given reservoir object and not other + ! modules or areas of the system. + type, abstract :: reservoir_state + + end type + + ! Defines the reservoir base properties/parameters type that + ! sub-types of reservoirs will derive from. Since this is abstract, + ! an instance of reservoir properties cannot be created but only + ! derived to sub-types of reservoirs. Properties holds + ! static/unchanging variables that are set when the given reservoir + ! object is initialized/instantiated. + type, abstract :: reservoir_properties + + end type + + ! Defines the input base type which holds the same inputs that all + ! sub-types of reservoirs will derive from. Therefore, all types of + ! reservoirs will hold the inputs defined below. These are + ! dynamic/changing variables that will receive values passed into the + ! object's methods/subroutines at runtime. + type :: reservoir_input + + real :: inflow ! cubic meters per second (cms) + real :: lateral_inflow ! cubic meters per second (cms) + real :: previous_timestep_inflow ! cubic meters per second (cms) + + contains + + procedure :: init => reservoir_input_init + procedure :: destroy => reservoir_input_destroy + + end type + + ! Defines the output base type which holds the same output(s) that all + ! sub-types of reservoirs will derive from. Therefore, all types of + ! reservoirs will hold the output(s) defined below. These are + ! dynamic/changing variables that will receive values returned from the + ! object's methods/subroutines at runtime. + type :: reservoir_output + + real :: outflow ! cubic meters per second (cms) + + contains + + procedure :: init => reservoir_output_init + procedure :: destroy => reservoir_output_destroy + + end type + + ! Defines the reservoir base type that sub-types of reservoirs + ! will derive from. Since this is abstract, an instance of reservoir + ! cannot be created but only derived to sub-types of reservoirs. + ! Derived sub-types of reservoirs of this base type will create a + ! reservoir object that will hold state, properties/parameters, + ! inputs, and outputs as sub-objects. + type, abstract :: reservoir + + type(reservoir_input), pointer :: input + type(reservoir_output), pointer :: output + + contains + + ! Defines procedure to call run reservoir for a type of reservoir, + ! which will then call methods/subroutines for processing the + ! inputs and returning the outputs. Since this is deferred, + ! it cannot be implemented for a base reservoir but only for + ! a sub-type of reservoir. + procedure (run_reservoir_interface), deferred :: run + + end type + + ! Defines a pointer to a reservoir base type + type :: reservoir_container + class (reservoir), pointer :: ptr + end type + + ! Defines the abstract implementation of run_reservoir that all + ! reservoir sub-types will derive from with the same given + ! inputs and outputs. + abstract interface + subroutine run_reservoir_interface(this, previous_timestep_inflow, inflow, & + lateral_inflow, water_elevation, outflow, routing_period, dynamic_reservoir_type, & + assimilated_value, assimilated_source_file) + import reservoir + class(reservoir), intent(inout) :: this + real, intent(in) :: previous_timestep_inflow ! cubic meters per second (cms) + real, intent(in) :: inflow ! cubic meters per second (cms) + real, intent(in) :: lateral_inflow ! cubic meters per second (cms) + real, intent(inout) :: water_elevation ! meters AMSL + real, intent(out) :: outflow ! cubic meters per second (cms) + real, intent(in) :: routing_period ! seconds + integer, intent(out):: dynamic_reservoir_type ! dynamic reservoir type sent to lake out files + real, intent(out) :: assimilated_value ! value assimilated from observation or forecast + character(len=256), intent(out) :: assimilated_source_file ! source file of assimilated value + + end subroutine run_reservoir_interface + + end interface + +contains + + ! Constructor the reservoir input type + subroutine reservoir_input_init(this) + implicit none + class (reservoir_input), intent(inout) :: this ! object being initialized + this%inflow = 0.0 + this%lateral_inflow = 0.0 + this%previous_timestep_inflow = 0.0 + + end subroutine reservoir_input_init + + + ! Destructor for the reservoir input type + subroutine reservoir_input_destroy(this) + implicit none + class (reservoir_input), intent(inout) :: this ! object being destroyed + + end subroutine reservoir_input_destroy + + + ! Constructor for the reservoir output type + subroutine reservoir_output_init(this) + implicit none + class (reservoir_output), intent(inout) :: this ! object being initialized + this%outflow = 0.0 + + end subroutine reservoir_output_init + + + ! Destructor for the reservoir output type + subroutine reservoir_output_destroy(this) + implicit none + class (reservoir_output), intent(inout) :: this ! object being destroyed + + end subroutine reservoir_output_destroy + +end module module_reservoir diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_rfc_time_series_data.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_rfc_time_series_data.F new file mode 100644 index 000000000..df4bf320f --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_rfc_time_series_data.F @@ -0,0 +1,237 @@ +! This module searches for and reads RFC Time Series files +! to get an array of observed and forecasted discharges that +! will be used by reservoirs. The forecast lookback hours +! period is passed in to determine how far back in time from +! the current model time plus another offset time to search +! ahead of model time the module will look for time series files. +! The forecast_file_resolution_minutes determines the time +! increments the module will look back. +module module_reservoir_read_rfc_time_series_data + use module_reservoir_utilities, only: read_timeslice_netcdf_integer_variables, & + read_timeslice_netcdf_real_1D_variables, & + read_timeslice_netcdf_integer_1D_variables, & + geth_newdate, & + handle_err + use netcdf + implicit none + + type :: time_series_data_type + + character(len=19) :: start_date + character(len=19) :: current_date + character(len=256) :: time_series_path + character(len=256) :: time_series_file_name + integer :: forecast_lookback_hours + integer :: timeslice_offset_hours + integer :: current_lookback_seconds + character(len=5) :: rfc_gage_id + integer :: total_counts + integer :: observed_counts + integer :: time_step_seconds + real, allocatable, dimension(:) :: discharges + integer, allocatable, dimension(:) :: synthetic_values + logical, allocatable, dimension(:) :: synthetic_values_logical + logical :: forecast_found + logical :: all_discharges_synthetic + + contains + + procedure :: init => time_series_data_init + procedure :: destroy => time_series_data_destroy + procedure :: setup_read_time_series => setup_read_time_series + procedure :: read_time_series_file => read_time_series_file + + end type time_series_data_type + + type (time_series_data_type) :: time_series_data + + integer, parameter :: forecast_file_resolution_minutes = 60 + +contains + + ! Time Series Data Type Constructor + subroutine time_series_data_init(this, start_date, time_series_path, forecast_lookback_hours, & + timeslice_offset_hours, rfc_gage_id, lake_number, lookback_seconds, total_counts, & + observed_counts, time_step_seconds, discharges, forecast_found, all_discharges_synthetic, & + time_series_file_name) + implicit none + class(time_series_data_type), intent(inout) :: this ! object being initialized + character(len=19), intent(in) :: start_date + character(len=*), intent(in) :: time_series_path + integer, intent(in) :: forecast_lookback_hours + integer, intent(in) :: timeslice_offset_hours + character(len=5), intent(in) :: rfc_gage_id + integer, intent(in) :: lake_number + integer, intent(out) :: lookback_seconds + integer, intent(out) :: total_counts + integer, intent(out) :: observed_counts + integer, intent(out) :: time_step_seconds + real, allocatable, dimension(:), intent(inout) :: discharges + logical, intent(inout) :: forecast_found + logical, intent(inout) :: all_discharges_synthetic + character(len=256), intent(out) :: time_series_file_name + character(len=15) :: lake_number_string + + this%forecast_found = .false. + this%all_discharges_synthetic = .false. + this%start_date = start_date + this%current_date = start_date + this%time_series_path = time_series_path + this%forecast_lookback_hours = forecast_lookback_hours + this%timeslice_offset_hours = timeslice_offset_hours + this%rfc_gage_id = rfc_gage_id + this%time_series_file_name = "" + + this%current_lookback_seconds = 0 + this%total_counts = 0 + this%observed_counts = 0 + this%time_step_seconds = 0 + + ! Call subroutine to search for and read an RFC time series file + call this%setup_read_time_series() + + lookback_seconds = this%current_lookback_seconds + total_counts = this%total_counts + observed_counts = this%observed_counts + time_step_seconds = this%time_step_seconds + + ! If the time series forecast is found + if (this%forecast_found) then + allocate(discharges(total_counts)) + + discharges = this%discharges + + ! Check if all of the values are synthetic + if ( all(this%synthetic_values_logical) .eqv. .true.) then + this%all_discharges_synthetic = .true. + end if + + ! Deallocate forecast arrays + if(allocated(this%discharges)) deallocate(this%discharges) + if(allocated(this%synthetic_values)) deallocate(this%synthetic_values) + if(allocated(this%synthetic_values_logical)) deallocate(this%synthetic_values_logical) + + end if + + forecast_found = this%forecast_found + all_discharges_synthetic = this%all_discharges_synthetic + time_series_file_name = this%time_series_file_name + + end subroutine time_series_data_init + + + ! Time Series Data Type Destructor + subroutine time_series_data_destroy(this) + + implicit none + class(time_series_data_type), intent(inout) :: this ! object being destroyed + + end subroutine time_series_data_destroy + + + ! Set up list of time series files to read + subroutine setup_read_time_series(this) + implicit none + + class(time_series_data_type), intent(inout) :: this + character(len=256) :: time_series_file_name + character(len=19) :: old_date, new_date + character(len=13) :: old_date_trimmed + character(len=2) :: forecast_file_resolution_string + integer :: total_time_series_file_periods, time_series_file_index + integer :: forecast_file_minute, forecast_file_resolution_seconds, timeslice_offset_seconds + logical :: file_exists + + this%current_lookback_seconds = 0 + + ! Determine the total number of time series files to search for + total_time_series_file_periods = this%forecast_lookback_hours * (60 / forecast_file_resolution_minutes) + + timeslice_offset_seconds = this%timeslice_offset_hours * 3600 + + old_date = this%current_date + + ! Get the new date-time ahead of model start time to start searching + ! for time series files if the timeslice_offset_seconds is greater than 0. + call geth_newdate(new_date, old_date, timeslice_offset_seconds) + old_date = new_date + + write(forecast_file_resolution_string, "(I2)") forecast_file_resolution_minutes + + ! Negative for going back in time + forecast_file_resolution_seconds = forecast_file_resolution_minutes * 60 * -1 + + ! Loop through the total time series periods to look for and read time series files + do time_series_file_index = 1, total_time_series_file_periods + + old_date_trimmed = trim(old_date(:13)) + + time_series_file_name = trim(this%time_series_path) // "/" // old_date_trimmed // "." // & + forecast_file_resolution_string // 'min.' // this%rfc_gage_id // '.RFCTimeSeries.ncdf' + + ! Check if file exists + inquire(FILE = time_series_file_name, EXIST = file_exists) + if (file_exists) then + + ! Call subroutine to read a particular time series file + call this%read_time_series_file(time_series_file_name) + + this%time_series_file_name = old_date_trimmed // "." // & + forecast_file_resolution_string // 'min.' // this%rfc_gage_id // '.RFCTimeSeries.ncdf' + + exit + end if + + ! Call subroutine to get the date from one forecast file resolution back in time + call geth_newdate(new_date, old_date, forecast_file_resolution_seconds) + old_date = new_date + + ! Lookback seconds to account for how long back a time series file was read + this%current_lookback_seconds = this%current_lookback_seconds - forecast_file_resolution_seconds + + end do + + end subroutine setup_read_time_series + + ! Read given time series file to get gage discharges + subroutine read_time_series_file(this, time_series_file) + implicit none + class(time_series_data_type), intent(inout) :: this + character(len=256), intent(in) :: time_series_file + integer*8, allocatable, dimension(:) :: gage_ids_integer_array + integer :: reservoir_rfc_gage_index, time_series_rfc_gage_index + integer :: ncid, status + + ! Open Time Series NetCDF file + status = nf90_open(path = trim(time_series_file), mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open RFC time series file " & + // trim(ADJUSTL(time_series_file)) // ".") + + call read_timeslice_netcdf_integer_variables(ncid, 'totalCounts', time_series_file, this%total_counts) + + allocate (this%discharges(this%total_counts)) + + allocate (this%synthetic_values(this%total_counts)) + + allocate (this%synthetic_values_logical(this%total_counts)) + + call read_timeslice_netcdf_integer_variables(ncid, 'observedCounts', time_series_file, this%observed_counts) + + call read_timeslice_netcdf_integer_variables(ncid, 'timeSteps', time_series_file, this%time_step_seconds) + + call read_timeslice_netcdf_real_1D_variables(ncid, 'discharges', time_series_file, this%discharges) + + call read_timeslice_netcdf_integer_1D_variables(ncid, 'synthetic_values', time_series_file, this%synthetic_values) + + this%synthetic_values_logical = this%synthetic_values + + this%forecast_found = .true. + + ! Close Time Series NetCDF file + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close RFC time series file " & + // trim(ADJUSTL(time_series_file)) // ".") + + end subroutine read_time_series_file + +end module module_reservoir_read_rfc_time_series_data diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_timeslice_data.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_timeslice_data.F new file mode 100644 index 000000000..b5919518c --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_read_timeslice_data.F @@ -0,0 +1,357 @@ +! This module reads USGS (U.S. Geological Survey) or USACE +! (U.S. Army Corps of Engineers) timeslice files to get gage discharge +! values that will be used by reservoirs. An observation lookback +! period is passed in to determine how far back in time from the +! current model time the module will look for timeslice files. +! The observation resolution determines the time increments the +! module will look back. For instance, a standard lookback period +! would be 18 hours with an observation resolution of 15 minutes, +! where a model current time of 8:00 PM would search for timeslice +! files at every 15 minute increment between 2:00 AM and 8:00 PM +! that day. The module will first search for the most recent +! timeslice files and grab the discharge for a particular +! lake/reservoir if the gage quality standard is met at that time. +! If a gage discharge is missing or if the gage quality standard +! is not met for any particular lake/reservoir in the given +! timeslice file, the module will continue to look back at every +! observation resolution increment until either all +! lakes/reservoirs have a good quality discharge or the end of +! the lookback period is reached. The total lookback seconds +! from current model time that the discharge is read will also +! be returned. + +module module_reservoir_read_timeslice_data + use module_reservoir_utilities, only: get_timeslice_array_dimension, & + read_timeslice_gage_ids, & + read_timeslice_netcdf_real_1D_variables, & + read_persistence_netcdf_gage_ids_all, & + geth_newdate, & + handle_err + use netcdf + implicit none + + ! The timeslice_data is a singleton in that the object is instantiated only once per + ! processor by the first reservoir on that processor that needs it. The object is then used + ! by every other reservoir on that processor that needs it. + type :: timeslice_data_type + + character(len=19) :: start_date + character(len=19) :: current_date + character(len=256) :: timeslice_path + character(len=15) :: data_source + integer :: number_of_reservoir_gages + integer :: observation_lookback_hours + integer :: current_lookback_seconds + integer :: last_update_time_seconds + integer, allocatable, dimension(:) :: gage_lookback_seconds + character(len=15), allocatable, dimension(:) :: reservoir_gage_ids + real, allocatable, dimension(:) :: reservoir_gage_discharges + character(len=256), allocatable, dimension(:) :: timeslice_file_names + character(len=15), allocatable, dimension(:) :: gage_id + real, allocatable, dimension(:) :: gage_discharge + real, allocatable, dimension(:) :: gage_quality + logical :: initialized = .FALSE. + + contains + + procedure :: init => timeslice_data_init + procedure :: destroy => timeslice_data_destroy + procedure :: setup_read_timeslice => setup_read_timeslice + procedure :: read_timeslice_file => read_timeslice_file + + end type timeslice_data_type + + type (timeslice_data_type), target :: usgs_timeslice_data, usace_timeslice_data + + integer, parameter :: observation_resolution_minutes = 15 + real, parameter :: gage_quality_threshold = 0.9 + +contains + + ! Timeslice Data Type Constructor + subroutine timeslice_data_init(this, start_date, timeslice_path, & + reservoir_parameter_file, data_source, observation_lookback_hours) + implicit none + class(timeslice_data_type), intent(inout) :: this ! object being initialized + character(len=19), intent(in) :: start_date + character(len=*), intent(in) :: timeslice_path + character(len=*), intent(in) :: reservoir_parameter_file + character(len=*), intent(in) :: data_source + integer, intent(in) :: observation_lookback_hours + integer :: ncid, var_id + integer :: status ! status of reading NetCDF + integer :: timeslice_gage_index + character(len=15), allocatable, dimension(:) :: gage_ids_string_array + character(len=15) :: gage_id_string, gage_id_string_trimmed + integer :: gage_id_integer + integer :: char_index, number_counter, temp_val + character(len=1) :: temp_char + + ! Return from subroutine if this singleton is already initialized + if (this%initialized) return + this%initialized = .true. + + this%data_source = ADJUSTL(trim(data_source)) + + status = nf90_open(path = reservoir_parameter_file, mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + call read_persistence_netcdf_gage_ids_all(ncid, ADJUSTL(trim(this%data_source)) // "_gage_id", & + reservoir_parameter_file, var_id, this%number_of_reservoir_gages) + + allocate (this%reservoir_gage_ids(this%number_of_reservoir_gages)) + allocate (gage_ids_string_array(this%number_of_reservoir_gages)) + + status = nf90_get_var(ncid, var_id, gage_ids_string_array) + if (status /= nf90_noerr) call handle_err(status, "Error reading " // ADJUSTL(trim(this%data_source)) & + // "_gage_id from " // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Convert gage ids to integers + do timeslice_gage_index = 1, this%number_of_reservoir_gages + gage_id_string = gage_ids_string_array(timeslice_gage_index) + + do char_index = 1, 15 + temp_val = ichar(gage_id_string(char_index:char_index)) + + ! Check if null character, then pad with a space + if (temp_val == 0) then + gage_id_string(char_index:char_index) = ' ' + end if + end do + + gage_id_string_trimmed = ADJUSTL(trim(gage_id_string)) + + this%reservoir_gage_ids(timeslice_gage_index) = gage_id_string_trimmed + + end do + + if(allocated(gage_ids_string_array)) deallocate(gage_ids_string_array) + + this%start_date = start_date + this%current_date = start_date + this%timeslice_path = timeslice_path + this%observation_lookback_hours = observation_lookback_hours + + this%last_update_time_seconds = -999 + + ! Allocate lookback, dishcharge, and timeslice name arrays + allocate (this%gage_lookback_seconds(this%number_of_reservoir_gages)) + allocate (this%reservoir_gage_discharges(this%number_of_reservoir_gages)) + allocate(this%timeslice_file_names(this%number_of_reservoir_gages)) + + end subroutine timeslice_data_init + + + ! Timeslice Data Type Destructor + subroutine timeslice_data_destroy(this) + + implicit none + class(timeslice_data_type), intent(inout) :: this ! object being destroyed + + end subroutine timeslice_data_destroy + + + ! Set up list of timeslice files to read + ! The timeslices are read only once on each processor at each timeslice update time + subroutine setup_read_timeslice(this, update_interval_seconds, current_reservoir_time_seconds, & + reservoir_gage_id, gage_lookback_seconds, reservoir_gage_discharge, reservoir_timeslice_file_name) + implicit none + + class(timeslice_data_type), intent(inout) :: this + integer, intent(in) :: update_interval_seconds + integer, intent(in) :: current_reservoir_time_seconds + character(len=*), intent(in) :: reservoir_gage_id + integer, intent(out) :: gage_lookback_seconds + real, intent(out) :: reservoir_gage_discharge + character(len=256), intent(out) :: reservoir_timeslice_file_name + character(len=256) :: timeslice_file_name + character(len=256) :: timeslice_file_name_full + character(len=19) :: old_date, new_date + character(len=2) :: observation_resolution_string + integer :: total_timeslice_time_periods, timeslice_file_index, reservoir_subset_index, lake_index + integer :: observation_minute, observation_resolution_seconds + logical :: file_exists + + ! This check ensures that the timeslice is read only once for each processor at each timeslice update time + if (current_reservoir_time_seconds .ne. this%last_update_time_seconds) then + + ! Checks if not the first timestep, then update date. + if (this%last_update_time_seconds .ne. -999) then + call geth_newdate(this%current_date, this%current_date, update_interval_seconds) + end if + + this%last_update_time_seconds = current_reservoir_time_seconds + + ! Set gage discharges of subset lakes to default -1.0 + this%reservoir_gage_discharges = -1.0 + + this%gage_lookback_seconds = 0 + this%current_lookback_seconds = 0 + + ! Set timeslice file names to empty strings by default + this%timeslice_file_names = "" + + total_timeslice_time_periods = this%observation_lookback_hours * (60 / observation_resolution_minutes) + + old_date = this%current_date + + read (old_date(15:16), *) observation_minute + + ! Match minutes to proper observation resolution interval + observation_minute = observation_minute / observation_resolution_minutes * observation_resolution_minutes + + ! Formatting for writing minutes + if (observation_minute < 10) then + old_date(15:16) = "00" + else + write(old_date(15:16), "(I2)") observation_minute + end if + + ! Zero out seconds + old_date(18:19) = "00" + + write(observation_resolution_string, "(I2)") observation_resolution_minutes + + ! Negative for going back in time + observation_resolution_seconds = observation_resolution_minutes * 60 * -1 + + ! Loop through the total timeslice periods to look for and read timeslice files + do timeslice_file_index = 1, total_timeslice_time_periods + + ! Use below for timeslice files with colons in the time + old_date = old_date(:13) // ':' // old_date(15:16) // ':' // old_date(18:) + + ! If any gages that still have discharges equal to -1.0, read the previous + ! time timeslice file available. + if ( any(this%reservoir_gage_discharges == -1.0)) then + + ! Construct timeslice filename + timeslice_file_name = old_date // "." // & + observation_resolution_string // 'min.' // ADJUSTL(trim(this%data_source)) // 'TimeSlice.ncdf' + + ! Add path to timeslice filename + timeslice_file_name_full = trim(this%timeslice_path) // "/" // ADJUSTL(trim(timeslice_file_name)) + + ! Check if file exists + inquire(FILE = timeslice_file_name_full, EXIST = file_exists) + if (file_exists) then + + ! Call subroutine to read a particular timeslice file + call this%read_timeslice_file(timeslice_file_name_full, timeslice_file_name) + end if + + ! Call subroutine to get the date from one observation resolution back in time + call geth_newdate(new_date, old_date, observation_resolution_seconds) + old_date = new_date + + ! Lookback seconds to account for how long back a timeslice file was read + this%current_lookback_seconds = this%current_lookback_seconds - observation_resolution_seconds + + else + exit + end if + end do + + end if + + ! Loop through gages to return gage lookback seconds and gage discharge for a given reservoir + do reservoir_subset_index = 1, this%number_of_reservoir_gages + if (this%reservoir_gage_ids(reservoir_subset_index) .eq. reservoir_gage_id) then + gage_lookback_seconds = this%gage_lookback_seconds(reservoir_subset_index) + reservoir_gage_discharge = this%reservoir_gage_discharges(reservoir_subset_index) + reservoir_timeslice_file_name = this%timeslice_file_names(reservoir_subset_index) + end if + end do + + end subroutine setup_read_timeslice + + + ! Read given timeslice file to get gage discharges + subroutine read_timeslice_file(this, timeslice_file_full, timeslice_file) + implicit none + class(timeslice_data_type), intent(inout) :: this + character(len=256), intent(in) :: timeslice_file_full + character(len=256), intent(in) :: timeslice_file + integer :: reservoir_gage_index, timeslice_gage_index, number_of_gages + integer :: ncid, status + + ! Open Timeslice NetCDF file + status = nf90_open(path = trim(timeslice_file_full), mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open timeslice file " & + // trim(ADJUSTL(timeslice_file_full)) // ".") + + ! Get dimension of gage arrays + call get_timeslice_array_dimension(ncid, 'discharge', timeslice_file_full, number_of_gages) + + ! Allocate gage info arrays + allocate(this%gage_id(number_of_gages)) + allocate(this%gage_discharge(number_of_gages)) + allocate(this%gage_quality(number_of_gages)) + + ! Get gage ids + call read_timeslice_gage_ids(ncid, 'stationId', timeslice_file_full, this%gage_id) + + ! Get gage discharges + call read_timeslice_netcdf_real_1D_variables(ncid, 'discharge', timeslice_file_full, this%gage_discharge) + + ! Get gage qualities + call read_timeslice_netcdf_real_1D_variables(ncid, 'discharge_quality', timeslice_file_full, this%gage_quality) + + ! Normalize gage qualities to 1 + this%gage_quality = this%gage_quality/100 + + ! First, quality check on the quality flag + where(this%gage_quality .lt. 0 .or. this%gage_quality .gt. 1) this%gage_quality=0 + + ! Currently using line below that would set quality to 0 if there is a negative + ! discharge but there may actually be a deficit in flow, i.e. no flow available + ! at all that the transducer has given us a negative pressure. We do not account + ! for that case here but may need to consider this later. + where(this%gage_discharge .lt. 0.000) this%gage_quality=0 + + ! Currently not using line below instead of above line that sets quality to 0 for negative discharge + !where(this%gage_discharge .le. 0.000) this%gage_discharge = 0.0 + + !! peak flow on MS river *2 + !! http://nwis.waterdata.usgs.gov/nwis/peak?site_no=07374000&agency_cd=USGS&format=html + !! baton rouge 1945: 1,473,000cfs=41,711cms, multiply it roughly by 2 + where(this%gage_discharge .ge. 90000.0) this%gage_quality=0 + + ! Loop through reservoir gage array to look for gages that still have discharges set to -1.0. + ! Match gage ids from the timeslice file gage id arrays to gage ids in the reservoir gage subset + ! array. If the matched gage is good quality, then set that reservoir gage subset discharge to + ! the discharge read from the timeslice file along with the total lookback seconds from current + ! model time that the gage discharge is read from a timeslice file. + do reservoir_gage_index = 1, this%number_of_reservoir_gages + if (this%reservoir_gage_discharges(reservoir_gage_index) == -1.0) then + do timeslice_gage_index = 1, number_of_gages + if (ADJUSTL(trim(this%gage_id(timeslice_gage_index))) & + .eq. ADJUSTL(trim(this%reservoir_gage_ids(reservoir_gage_index)))) then + if (this%gage_quality(timeslice_gage_index) .gt. gage_quality_threshold) then + this%reservoir_gage_discharges(reservoir_gage_index) = this%gage_discharge(timeslice_gage_index) + this%gage_lookback_seconds(reservoir_gage_index) = this%current_lookback_seconds + this%timeslice_file_names(reservoir_gage_index) = ADJUSTL(trim(timeslice_file)) + end if + exit + end if + end do + end if + end do + + ! Close timeslice NetCDF file + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close timeslice file " // trim(ADJUSTL(timeslice_file_full)) // ".") + + ! Deallocate gage arrays + if(allocated(this%gage_id)) deallocate(this%gage_id) + if(allocated(this%gage_discharge)) deallocate(this%gage_discharge) + if(allocated(this%gage_quality)) deallocate(this%gage_quality) + + end subroutine read_timeslice_file + +end module module_reservoir_read_timeslice_data diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_utilities.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_utilities.F new file mode 100644 index 000000000..40b08c642 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/module_reservoir_utilities.F @@ -0,0 +1,1196 @@ +! This module contains a variety of subroutines/functions +! used by reservoir objects. + +module module_reservoir_utilities + use netcdf + use module_hydro_stop, only: HYDRO_stop + implicit none + +#ifndef NCEP_WCOSS + integer, parameter :: log_warning = 6 +#else + integer, parameter :: log_warning = 78 +#endif + +contains + + ! Checks and warns for boundary conditions of negative inflow + subroutine warn_negative_inflow(inflow, lake_number, current_time) + real, intent(in) :: inflow + integer, intent(in) :: lake_number, current_time + + if (inflow < 0.0) then + write(log_warning,*) "WARNING: Current inflow ", inflow, & + " cubic meters per second has reached below zero for reservoir ", & + lake_number, " at ", current_time, " seconds after model start time." + end if + + end subroutine warn_negative_inflow + + + ! Checks and warns for boundary conditions of exceeding max or min storage + subroutine warn_storage_boundaries(inflow, end_time_storage, min_storage, max_storage, lake_number, current_time) + real, intent(in) :: inflow, end_time_storage, min_storage, max_storage + integer, intent(in) :: lake_number, current_time + + ! Also have option to set the end_time_storage to the max_storage and change the outflow accordingly. + if (end_time_storage > max_storage) then + + write(log_warning,*) "WARNING: Calculated storage for this timestep, ", end_time_storage, & + " cubic meters, will exceed the maximum storage boundary of: ", max_storage, & + " cubic meters, for reservoir ", lake_number," at ", current_time, " seconds after model start time." + write(log_warning,*) "Current inflow is ", inflow, " cubic meters per second." + + ! Also have option to set the end_time_storage to the min_storage and change the outflow accordingly. + else if (end_time_storage < min_storage .and. end_time_storage > 0.0) then + + write(log_warning,*) "WARNING: Calculated storage for this timestep, ", end_time_storage, & + " cubic meters, will fall below the minimum storage requirement of: ", min_storage, & + " cubic meters, for reservoir ", lake_number, " at ", current_time, " seconds after model start time." + write(log_warning,*) "Current inflow is ", inflow, " cubic meters per second." + + end if + + end subroutine warn_storage_boundaries + + + ! Checks and modifies for boundary conditions of negative storage + subroutine modify_negative_storage(inflow, previous_time_storage, routing_period, lake_number, current_time, & + update_time, end_time_storage, outflow) + real, intent(in) :: inflow, previous_time_storage, routing_period + integer, intent(in) :: lake_number, current_time, update_time + real, intent(inout) :: end_time_storage, outflow + + ! Note to user: + ! In order to conserve mass, when a reservoir's storage would become 0.0 or less, the release + ! must be adjusted to avoid releasing water that is not available in the reservoir. To determine + ! conditions when this could happen, this function calculates the projected by the end of the + ! timestep (dt) based on the latest known conditions of the storage, inflow, and release values. + + ! If the current conditions would cause the storage to become less than or equal to 0.0 cubic meters, + ! then the outflow/release will be altered to provide the available storage over the timestep + ! (given as R = (S + I*dt)/dt ). This will cause storage at the end of the timestep to be 0.0. From + ! this point, the outflow/release is bounded by the inflow provided until the next update time when + ! the machine learning model will be run again. + + ! Therefore, the user's choice of time_interval has a significant impact on this simulated outflow/release + ! behavior in this edge case. Release cannot increase again until the next update time occurs. This does, + ! however, allow the reservoir to fill back up once inflow increases to exceed current outflow/release + ! since storage would then become > 0.0. + + ! If one wants to allow the release to continue to rise as inflow rises, you can set R = I*dt once storage + ! has been depleted. This passes on inflow to outflow until an update time occurs, in which case the ML + ! model and its post conditions are responsible for calculating an appropriate release. + + ! The logical 'and' below is not necessary because if otherwise this check updates outflow and + ! end_time_storage before an update time, those variables would be overwritten after a new release + ! is calculated from the machine learning model. The logical 'and' is left here for emphasis on + ! the importance it has on timesteps that are not also update times. + if (end_time_storage <= 0.0 .AND. current_time < update_time) then + + write(log_warning,*) "WARNING: End time storage, ", end_time_storage, & + " cubic meters, has reached at or below 0.0 cubic meters for reservoir ", lake_number, & + " at ", current_time, " seconds after model start time." + write(log_warning,*) "Current inflow is ", inflow, " cubic meters per second." + + outflow = (previous_time_storage + inflow * routing_period) / routing_period + + end_time_storage = 0.0 + + if (outflow < 0.0) outflow = 0.0 + + end if + + end subroutine modify_negative_storage + + + ! Checks and modifies for boundary conditions of negative inflow + subroutine modify_negative_inflow(inflow) + real, intent(inout) :: inflow + + if (inflow < 0.0) inflow = 0.0 + + end subroutine modify_negative_inflow + + + ! Checks and modifies for boundary conditions of negative outflow or exceeding min or max storage + subroutine modify_for_projected_storage(inflow, previous_time_storage, min_storage, max_storage, & + lake_number, current_time, time_interval, outflow, max_storage_reached) + real, intent(in) :: inflow, previous_time_storage, min_storage, max_storage + integer, intent(in) :: lake_number, current_time, time_interval + real, intent(inout) :: outflow + logical, intent(inout) :: max_storage_reached + real :: excess, projected_next_time_interval_storage + + if (outflow < 0.0) then + write(log_warning,*) "WARNING: Calculations return a negative outflow for reservoir ", lake_number + write(log_warning,*) "at ", current_time, " seconds after model start time." + outflow = 0.0 + end if + + ! The projected_next_time_interval_storage is only calculated as a boundary condition to update the release + ! and this storage is calculated assuming a constant flow rate over the time interval. If the time interval + ! is too large relative to the channel routing period, this may not be an appropriate calculation. + projected_next_time_interval_storage = previous_time_storage + (inflow - outflow) * time_interval + + if (projected_next_time_interval_storage > max_storage) then + + max_storage_reached = .true. + + write(log_warning,*) "WARNING: Modified release to prevent maximum storage exceedance", & + " for reservoir ", lake_number + write(log_warning,*) "at ", current_time, " seconds after model start time." + + else if (projected_next_time_interval_storage < min_storage & + .AND. projected_next_time_interval_storage > 0.0) then + + outflow = (projected_next_time_interval_storage - min_storage) / time_interval + outflow + + write(log_warning,*) "WARNING: Modified release to maintain minimum storage for reservoir ", lake_number + write(log_warning,*) "at ", current_time, " seconds after model start time." + + else if (projected_next_time_interval_storage <= 0.0) then + + outflow = inflow + + write(log_warning,*) "WARNING: Modified release to prevent storage deficit for reservoir ", lake_number + write(log_warning,*) "at ", current_time, " seconds after model start time." + + end if + + if (outflow < 0.0) outflow = 0.0 + + end subroutine modify_for_projected_storage + + ! Reads the reservoir_type form the reservoir parameter file. This subroutine first checks to ensure that + ! the lake_id array in the reservoir parameter file matches the lake_id array in the lake parameter file and + ! calls hydro_stop if they do not match. + subroutine read_reservoir_type(reservoir_parameter_file, lake_parameter_lake_ids, number_of_lake_parameter_lakes, & + reservoir_types) + character(len=*), intent(in) :: reservoir_parameter_file + integer, dimension(:), intent(in) :: lake_parameter_lake_ids + integer, intent(in) :: number_of_lake_parameter_lakes + integer, dimension(:), intent(inout) :: reservoir_types + integer, allocatable, dimension(:) :: reservoir_parameter_lake_ids + + integer, dimension(nf90_max_var_dims) :: dim_ids + integer :: ncid, var_id, lake_index, number_of_reservoirs, number_of_reservoir_types + integer :: status ! status of reading NetCDF + + ! Open Reservoir Parameter NetCDF file + status = nf90_open(path = reservoir_parameter_file, mode = nf90_nowrite, ncid = ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not open reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inq_varid(ncid, "lake_id", var_id) + if (status /= nf90_noerr) call handle_err(status, "lake_id read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, "lake_id read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_reservoirs) + if(status /= nf90_NoErr) call handle_err(status, "lake_id read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + allocate(reservoir_parameter_lake_ids(number_of_reservoirs)) + + status = nf90_get_var(ncid, var_id, reservoir_parameter_lake_ids) + if (status /= nf90_noerr) call handle_err(status, "lake_id read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Check to ensure that the lake_id array size in the reservoir parameter file matches the lake_id array + ! size in the lake parameter file + if (number_of_lake_parameter_lakes .ne. number_of_reservoirs) then + call hydro_stop("ERROR: Reservoir ID array in the reservoir parameter file does not match the Lake ID array & + in the lake parameter file.") + + else + ! Check to ensure that each lake id in the lake_id array in the reservoir parameter file matches + ! each lake id in the lake_id array in the lake parameter file + do lake_index = 1, number_of_reservoirs + + if (lake_parameter_lake_ids(lake_index) .ne. reservoir_parameter_lake_ids(lake_index)) then + call hydro_stop("Reservoir ID array in the reservoir parameter file does not match the Lake ID array & + in the lake parameter file.") + end if + end do + end if + + status = nf90_inq_varid(ncid, "reservoir_type", var_id) + if (status /= nf90_noerr) call handle_err(status, "reservoir_type read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, "reservoir_type read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_reservoir_types) + if(status /= nf90_NoErr) call handle_err(status, "reservoir_type read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Check to ensure that the reservoir_type array size matches the lake_id array size. + if (number_of_reservoir_types .ne. number_of_lake_parameter_lakes) then + call hydro_stop("ERROR: Reservoir Type array size in the reservoir parameter file does not match the Lake ID array size & + in the lake parameter file.") + end if + + status = nf90_get_var(ncid, var_id, reservoir_types) + if (status /= nf90_noerr) call handle_err(status, "reservoir_type read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + ! Close Reservoir Parameter NetCDF file + status = nf90_close(ncid) + if (status /= nf90_noerr) call handle_err(status, "Could not close reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + if(allocated(reservoir_parameter_lake_ids)) deallocate(reservoir_parameter_lake_ids) + + end subroutine read_reservoir_type + + + ! Gets the dimensions of the gage arrays from the timeslice file + subroutine get_timeslice_array_dimension(ncid, netcdf_array_name, timeslice_file, number_of_gages) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_array_name + character(len=256), intent(in) :: timeslice_file + integer, intent(out) :: number_of_gages + integer :: var_id, status + integer, dimension(nf90_max_var_dims) :: dim_ids + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_gages) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + end subroutine get_timeslice_array_dimension + + + ! Read gage id array from the timeslice NetCDF + subroutine read_timeslice_gage_ids(ncid, netcdf_array_name, timeslice_file, gage_id_var_array) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_array_name + character(len=256), intent(in) :: timeslice_file + character(len=*), dimension(:), intent(out) :: gage_id_var_array + integer :: var_id, status + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_get_var(ncid, var_id, gage_id_var_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + end subroutine read_timeslice_gage_ids + + + ! Read real one dimension parameter arrays from the timeslice NetCDF + subroutine read_timeslice_netcdf_real_1D_variables(ncid, netcdf_array_name, timeslice_file, real_var_array) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_array_name + character(len=256), intent(in) :: timeslice_file + real, dimension(:), intent(out) :: real_var_array + integer :: var_id, status + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_get_var(ncid, var_id, real_var_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + end subroutine read_timeslice_netcdf_real_1D_variables + + + ! Read integer one dimension parameter arrays from the timeslice NetCDF + subroutine read_timeslice_netcdf_integer_1D_variables(ncid, netcdf_array_name, timeslice_file, integer_var_array) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_array_name + character(len=256), intent(in) :: timeslice_file + integer, dimension(:), intent(out) :: integer_var_array + integer :: var_id, status + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_get_var(ncid, var_id, integer_var_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + end subroutine read_timeslice_netcdf_integer_1D_variables + + + ! Read all gage ids from reservoir parameter file + subroutine read_persistence_netcdf_gage_ids_all(ncid, netcdf_array_name, reservoir_parameter_file, var_id, number_of_gages) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + integer, intent(out) :: var_id, number_of_gages + integer :: status + integer, dimension(nf90_max_var_dims) :: dim_ids + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(2), len = number_of_gages) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + end subroutine read_persistence_netcdf_gage_ids_all + + + ! Read lake id from reservoir parameter file and determine the lake_id_index, which + ! is the index of the particular reservoir's values in all of the parameter arrays. + subroutine read_netcdf_lake_id(ncid, lake_number, netcdf_array_name, reservoir_parameter_file, lake_id_index) + integer, intent(in) :: ncid, lake_number + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + integer, intent(out) :: lake_id_index + integer, allocatable, dimension(:) :: temp_lake_id_array + integer :: var_id, status, number_of_lakes, lake_index + integer, dimension(nf90_max_var_dims) :: dim_ids + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name) + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_lakes) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + allocate(temp_lake_id_array(number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_lake_id_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + do lake_index = 1, number_of_lakes + if (temp_lake_id_array(lake_index) == lake_number) then + lake_id_index = lake_index + end if + end do + + if(allocated(temp_lake_id_array)) deallocate(temp_lake_id_array) + + end subroutine read_netcdf_lake_id + + + ! Read gage id array from reservoir parameter file + subroutine read_persistence_netcdf_gage_id(ncid, lake_id_index, netcdf_array_name, reservoir_parameter_file, gage_id) + integer, intent(in) :: ncid, lake_id_index + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + character(len=*), intent(out) :: gage_id + character(len=15) :: gage_id_string, gage_id_string_trimmed + character(len=15), allocatable, dimension(:) :: temp_gage_id_array + integer :: var_id, status, number_of_lakes + integer, dimension(nf90_max_var_dims) :: dim_ids + integer :: char_index, number_counter, temp_val + character(len=1) :: temp_char + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(2), len = number_of_lakes) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + allocate(temp_gage_id_array(number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_gage_id_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + gage_id_string = temp_gage_id_array(lake_id_index) + + do char_index = 1, 15 + temp_val = ichar(gage_id_string(char_index:char_index)) + + ! Check if null character, then pad with a space + if (temp_val == 0) then + gage_id_string(char_index:char_index) = ' ' + end if + end do + + gage_id_string_trimmed = ADJUSTL(trim(gage_id_string)) + + gage_id = gage_id_string_trimmed + + if(allocated(temp_gage_id_array)) deallocate(temp_gage_id_array) + + end subroutine read_persistence_netcdf_gage_id + + + ! Read rfc gage id array from the reservoir parameters file + subroutine read_reservoir_parameters_netcdf_rfc_gage_id(ncid, lake_id_index, & + netcdf_array_name, reservoir_parameter_file, rfc_gage_id) + integer, intent(in) :: ncid, lake_id_index + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + character(len=5), intent(out) :: rfc_gage_id + character(len=5) :: gage_id_string, gage_id_string_trimmed + character(len=5), allocatable, dimension(:) :: temp_gage_id_array + integer :: var_id, status, number_of_lakes + integer, dimension(nf90_max_var_dims) :: dim_ids + integer :: char_index, number_counter + character(len=1) :: temp_char + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name) + + status = nf90_inquire_dimension(ncid, dim_ids(2), len = number_of_lakes) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + allocate(temp_gage_id_array(number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_gage_id_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + gage_id_string = temp_gage_id_array(lake_id_index) + + gage_id_string_trimmed = ADJUSTL(trim(gage_id_string)) + + rfc_gage_id = gage_id_string_trimmed + + if(allocated(temp_gage_id_array)) deallocate(temp_gage_id_array) + + end subroutine read_reservoir_parameters_netcdf_rfc_gage_id + + + ! Read an RFC forecast integer variable for a corresponding lake id from the reservoir parameter file + subroutine read_reservoir_parameters_netcdf_rfc_integer(ncid, lake_id_index, & + netcdf_array_name, reservoir_parameter_file, integer_var) + integer, intent(in) :: ncid, lake_id_index + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + integer, intent(out) :: integer_var + integer, allocatable, dimension(:) :: temp_integer_array + integer :: var_id, status, number_of_lakes + integer, dimension(nf90_max_var_dims) :: dim_ids + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_lakes) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + allocate(temp_integer_array(number_of_lakes)) + + status = nf90_get_var(ncid, var_id, temp_integer_array) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + integer_var = temp_integer_array(lake_id_index) + + if(allocated(temp_integer_array)) deallocate(temp_integer_array) + + end subroutine read_reservoir_parameters_netcdf_rfc_integer + + + ! Read real two dimension parameter arrays from the reservoir parameter file + subroutine read_persistence_netcdf_real_2D_parameters(ncid, lake_id_index, & + netcdf_array_name, reservoir_parameter_file, var_id, number_of_weights, number_of_lakes) + integer, intent(in) :: ncid, lake_id_index + character(len=*), intent(in) :: netcdf_array_name + character(len=*), intent(in) :: reservoir_parameter_file + integer, intent(out) :: var_id, number_of_weights, number_of_lakes + integer :: status + integer, dimension(nf90_max_var_dims) :: dim_ids + + status = nf90_inq_varid(ncid, netcdf_array_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_variable(ncid, var_id, dimids = dim_ids) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(1), len = number_of_weights) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + status = nf90_inquire_dimension(ncid, dim_ids(2), len = number_of_lakes) + if(status /= nf90_NoErr) call handle_err(status, netcdf_array_name // " read error in reservoir parameter file " & + // trim(ADJUSTL(reservoir_parameter_file)) // ".") + + end subroutine read_persistence_netcdf_real_2D_parameters + + + ! Read integer parameters from the RFC Time Series NetCDF + subroutine read_timeslice_netcdf_integer_variables(ncid, netcdf_variable_name, timeslice_file, integer_variable) + integer, intent(in) :: ncid + character(len=*), intent(in) :: netcdf_variable_name + character(len=256), intent(in) :: timeslice_file + integer, intent(out) :: integer_variable + integer :: var_id, status + + status = nf90_inq_varid(ncid, netcdf_variable_name, var_id) + if (status /= nf90_noerr) call handle_err(status, netcdf_variable_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + status = nf90_get_var(ncid, var_id, integer_variable) + if (status /= nf90_noerr) call handle_err(status, netcdf_variable_name // " read error in timeslice file " & + // trim(ADJUSTL(timeslice_file)) // ".") + + end subroutine read_timeslice_netcdf_integer_variables + + + ! Handle NetCDF error + subroutine handle_err(status, variable_error) + implicit none + integer, intent (in) :: status + character (len=*), intent(in) :: variable_error + if(status /= nf90_noerr) then + call hydro_stop("ERROR: " // trim(nf90_strerror(status)) // ': ' // variable_error) + end if + + end subroutine handle_err + + + ! Create Persistence Levelpool Hybrid Reservoir Function Diagnostic Log CSV File + subroutine create_hybrid_diagnostic_log_file(lake_number) + integer, intent(in) :: lake_number + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + filename_string = "hybrid_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown") + + write (113, "(A256)") "Current Time, Timeslice Update Time, Weight Update Time, & + Gage Lookback Seconds, Gage ID, Current Persistence Weight Index, & + Current Persistence Weight, Inflow, Storage, Water Elevation, Gage Discharge, & + Persisted Outflow, Levelpool Outflow, Weighted Outflow" + + close(113) + + end subroutine create_hybrid_diagnostic_log_file + + + ! Log data to Persistence Levelpool Hybrid Reservoir Function Diagnostic Log CSV File + subroutine log_hybrid_diagnostic_data(lake_number, current_time, timeslice_update_time, & + weight_update_time, gage_lookback_seconds, gage_id, persistence_weight_index, & + persistence_current_weight, inflow, current_storage, water_elevation, gage_discharge, & + persisted_outflow, levelpool_outflow, outflow) + integer, intent(in) :: lake_number, current_time, timeslice_update_time + integer, intent(in) :: weight_update_time, gage_lookback_seconds + integer, intent(in) :: persistence_weight_index + real, intent(in) :: persistence_current_weight, inflow, current_storage + real, intent(in) :: water_elevation, gage_discharge, persisted_outflow + real, intent(in) :: levelpool_outflow, outflow + character(len=*), intent(in) :: gage_id + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + filename_string = "hybrid_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown", access = 'append') + + write (113, "(I20, A1, I20, A1, I20, A1, I20, A1, A15, A1, I20, A1, F15.5, A1, F15.5, & + A1, F15.15, A1, F15.5, A1, F15.5, A1, F15.5, A1, F15.5, A1, F15.5)") & + current_time, ',', timeslice_update_time, ',', weight_update_time, ',', & + gage_lookback_seconds, ',', gage_id, ',', persistence_weight_index, ',', & + persistence_current_weight, ',', inflow, ',', current_storage, ',', & + water_elevation, ',', gage_discharge, ',', persisted_outflow, ',', levelpool_outflow, & + ',', outflow + + close (113) + + end subroutine log_hybrid_diagnostic_data + + + ! Create RFC Forecasts Reservoir Function Diagnostic Log CSV File + subroutine create_rfc_forecasts_diagnostic_log_file(lake_number) + integer, intent(in) :: lake_number + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + filename_string = "rfc_forecasts_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown") + + write (113, "(A256)") "Current_Time, Time_Step, Time_Series_Update_Time, & + Lookback_Seconds, Time_Series_Index, Gage_ID, Inflow, Water_Elevation, & + Levelpool_Outflow, Outflow" + + close(113) + + end subroutine create_rfc_forecasts_diagnostic_log_file + + + ! Log data to RFC Forecasts Reservoir Function Diagnostic Log CSV File + subroutine log_rfc_forecasts_diagnostic_data(lake_number, current_time, time_step, & + time_series_update_time, lookback_seconds, time_series_index, gage_id, & + inflow, water_elevation, levelpool_outflow, outflow) + integer, intent(in) :: lake_number, current_time, time_step, time_series_update_time + integer, intent(in) :: lookback_seconds, time_series_index + character(len=*), intent(in) :: gage_id + real, intent(in) :: inflow, water_elevation, levelpool_outflow, outflow + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + filename_string = "rfc_forecasts_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown", access = 'append') + + write (113, "(I20, A1, I20, A1, I20, A1, I20, A1, I20, A1, A5, A1, F15.5, A1, & + F15.5, A1, F15.5, A1, F15.5)") & + current_time, ',', time_step, ',', time_series_update_time, ',', & + lookback_seconds, ',', time_series_index, ',', gage_id, ',', & + inflow, ',', water_elevation, ',', levelpool_outflow, ',', outflow + + close (113) + + end subroutine log_rfc_forecasts_diagnostic_data + + + ! Create Levelpool Reservoir Function Diagnostic Log CSV File + subroutine create_levelpool_diagnostic_log_file(lake_number) + integer, intent(in) :: lake_number + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + + filename_string = "levelpool_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown") + + write (113, "(A256)") "Inflow, Water Elevation, Outflow" + + close(113) + + end subroutine create_levelpool_diagnostic_log_file + + + ! Log data to Levelpool Reservoir Function Diagnostic Log CSV File + subroutine log_levelpool_diagnostic_data(lake_number, inflow, water_elevation, outflow) + integer, intent(in) :: lake_number + real, intent(in) :: inflow + real, intent(in) :: water_elevation + real, intent(in) :: outflow + character(len=15) :: lake_number_string, lake_number_string_trimmed + character(len=50) :: filename_string + + write(lake_number_string, "(I15)") lake_number + lake_number_string_trimmed = ADJUSTL(trim(lake_number_string)) + filename_string = "levelpool_logs_"//ADJUSTL(trim(lake_number_string_trimmed))//".csv" + + open (113, file=filename_string, status="unknown", access = 'append') + + write (113, "(F15.8, A1, F15.8, A1, F15.8)") & + inflow, ',', water_elevation, ',', outflow + + close (113) + + end subroutine log_levelpool_diagnostic_data + + + ! Get a new date given an existing date and time interval + subroutine geth_newdate (ndate, odate, idt) + implicit none + + ! From old date ("YYYY-MM-DD HH:MM:SS.ffff" or "YYYYMMDDHHMMSSffff") and + ! delta-time, compute the new date. + + ! on entry - odate - the old hdate. + ! idt - the change in time + + ! on exit - ndate - the new hdate. + + integer, intent(in) :: idt + character (len=*), intent(out) :: ndate + character (len=*), intent(in) :: odate + + ! Local Variables + + ! yrold - indicates the year associated with "odate" + ! moold - indicates the month associated with "odate" + ! dyold - indicates the day associated with "odate" + ! hrold - indicates the hour associated with "odate" + ! miold - indicates the minute associated with "odate" + ! scold - indicates the second associated with "odate" + + ! yrnew - indicates the year associated with "ndate" + ! monew - indicates the month associated with "ndate" + ! dynew - indicates the day associated with "ndate" + ! hrnew - indicates the hour associated with "ndate" + ! minew - indicates the minute associated with "ndate" + ! scnew - indicates the second associated with "ndate" + + ! mday - a list assigning the number of days in each month + + ! i - loop counter + ! nday - the integer number of days represented by "idt" + ! nhour - the integer number of hours in "idt" after taking out + ! all the whole days + ! nmin - the integer number of minutes in "idt" after taking out + ! all the whole days and whole hours. + ! nsec - the integer number of minutes in "idt" after taking out + ! all the whole days, whole hours, and whole minutes. + + integer :: newlen, oldlen + integer :: yrnew, monew, dynew, hrnew, minew, scnew, frnew + integer :: yrold, moold, dyold, hrold, miold, scold, frold + integer :: nday, nhour, nmin, nsec, nfrac, i, ifrc + logical :: opass + character (len=10) :: hfrc + character (len=1) :: sp + logical :: punct + integer :: yrstart, yrend, mostart, moend, dystart, dyend + integer :: hrstart, hrend, mistart, miend, scstart, scend, frstart + integer :: units + integer, dimension(12) :: mday = (/31,28,31,30,31,30,31,31,30,31,30,31/) + + ! Determine if odate is "YYYY-MM-DD_HH ... " or "YYYYMMDDHH...." + if (odate(5:5) == "-") then + punct = .TRUE. + else + punct = .FALSE. + endif + + ! Break down old hdate into parts + + hrold = 0 + miold = 0 + scold = 0 + frold = 0 + oldlen = LEN(odate) + if (punct) then + yrstart = 1 + yrend = 4 + mostart = 6 + moend = 7 + dystart = 9 + dyend = 10 + hrstart = 12 + hrend = 13 + mistart = 15 + miend = 16 + scstart = 18 + scend = 19 + frstart = 21 + select case (oldlen) + case (10) + ! Days + units = 1 + case (13) + ! Hours + units = 2 + case (16) + ! Minutes + units = 3 + case (19) + ! Seconds + units = 4 + case (21) + ! Tenths + units = 5 + case (22) + ! Hundredths + units = 6 + case (23) + ! Thousandths + units = 7 + case (24) + ! Ten thousandths + units = 8 + case default + + write(*,*) 'FATAL ERROR: geth_newdate: odd length: #'//trim(odate)//'#' + call hydro_stop("geth_newdate") + + end select + + if (oldlen.ge.11) then + sp = odate(11:11) + else + sp = ' ' + end if + + else + + yrstart = 1 + yrend = 4 + mostart = 5 + moend = 6 + dystart = 7 + dyend = 8 + hrstart = 9 + hrend = 10 + mistart = 11 + miend = 12 + scstart = 13 + scend = 14 + frstart = 15 + + select case (oldlen) + case (8) + ! Days + units = 1 + case (10) + ! Hours + units = 2 + case (12) + ! Minutes + units = 3 + case (14) + ! Seconds + units = 4 + case (15) + ! Tenths + units = 5 + case (16) + ! Hundredths + units = 6 + case (17) + ! Thousandths + units = 7 + case (18) + ! Ten thousandths + units = 8 + case default + + write(*,*) 'FATAL ERROR: geth_newdate: odd length: #'//trim(odate)//'#' + call hydro_stop("geth_newdate") + + end select + endif + + ! Use internal READ statements to convert the CHARACTER string + ! date into INTEGER components. + + read(odate(yrstart:yrend), '(i4)') yrold + read(odate(mostart:moend), '(i2)') moold + read(odate(dystart:dyend), '(i2)') dyold + if (units.ge.2) then + read(odate(hrstart:hrend),'(i2)') hrold + if (units.ge.3) then + read(odate(mistart:miend),'(i2)') miold + if (units.ge.4) then + read(odate(scstart:scend),'(i2)') scold + if (units.ge.5) then + read(odate(frstart:oldlen),*) frold + end if + end if + end if + end if + + ! Set the number of days in February for that year. + + mday(2) = nfeb(yrold) + + ! Check that ODATE makes sense. + + opass = .TRUE. + + ! Check that the month of ODATE makes sense. + + if ((moold.gt.12).or.(moold.lt.1)) then + write(*,*) 'GETH_NEWDATE: Month of ODATE = ', moold + opass = .FALSE. + end if + + ! Check that the day of ODATE makes sense. + + if ((dyold.gt.mday(moold)).or.(dyold.lt.1)) then + write(*,*) 'GETH_NEWDATE: Day of ODATE = ', dyold + opass = .FALSE. + end if + + ! Check that the hour of ODATE makes sense. + + if ((hrold.gt.23).or.(hrold.lt.0)) then + write(*,*) 'GETH_NEWDATE: Hour of ODATE = ', hrold + opass = .FALSE. + end if + + ! Check that the minute of ODATE makes sense. + + if ((miold.gt.59).or.(miold.lt.0)) then + write(*,*) 'GETH_NEWDATE: Minute of ODATE = ', miold + opass = .FALSE. + end if + + ! Check that the second of ODATE makes sense. + + if ((scold.gt.59).or.(scold.lt.0)) then + write(*,*) 'GETH_NEWDATE: Second of ODATE = ', scold + opass = .FALSE. + end if + + ! Check that the fractional part of ODATE makes sense. + + + if (.not.opass) then + + write(*,*) 'FATAL ERROR: Crazy ODATE: ', odate(1:oldlen), oldlen + stop + + end if + + ! Date Checks are completed. Continue. + + + ! Compute the number of days, hours, minutes, and seconds in idt + + if (units.ge.5) then !idt should be in fractions of seconds + ifrc = oldlen-(frstart)+1 + ifrc = 10**ifrc + nday = abs(idt)/(86400*ifrc) + nhour = mod(abs(idt),86400*ifrc)/(3600*ifrc) + nmin = mod(abs(idt),3600*ifrc)/(60*ifrc) + nsec = mod(abs(idt),60*ifrc)/(ifrc) + nfrac = mod(abs(idt), ifrc) + else if (units.eq.4) then !idt should be in seconds + ifrc = 1 + nday = abs(idt)/86400 ! integer number of days in delta-time + nhour = mod(abs(idt),86400)/3600 + nmin = mod(abs(idt),3600)/60 + nsec = mod(abs(idt),60) + nfrac = 0 + else if (units.eq.3) then !idt should be in minutes + ifrc = 1 + nday = abs(idt)/1440 ! integer number of days in delta-time + nhour = mod(abs(idt),1440)/60 + nmin = mod(abs(idt),60) + nsec = 0 + nfrac = 0 + else if (units.eq.2) then !idt should be in hours + ifrc = 1 + nday = abs(idt)/24 ! integer number of days in delta-time + nhour = mod(abs(idt),24) + nmin = 0 + nsec = 0 + nfrac = 0 + else if (units.eq.1) then !idt should be in days + ifrc = 1 + nday = abs(idt) ! integer number of days in delta-time + nhour = 0 + nmin = 0 + nsec = 0 + nfrac = 0 + else + + write(*,'(''GETH_NEWDATE: Strange length for ODATE: '', i3)') & + oldlen + write(*,*) '#'//odate(1:oldlen)//'#' + call hydro_stop("geth_newdate") + + end if + + if (idt.ge.0) then + + frnew = frold + nfrac + if (frnew.ge.ifrc) then + frnew = frnew - ifrc + nsec = nsec + 1 + end if + + scnew = scold + nsec + if (scnew .ge. 60) then + scnew = scnew - 60 + nmin = nmin + 1 + end if + + minew = miold + nmin + if (minew .ge. 60) then + minew = minew - 60 + nhour = nhour + 1 + end if + + hrnew = hrold + nhour + if (hrnew .ge. 24) then + hrnew = hrnew - 24 + nday = nday + 1 + end if + + dynew = dyold + monew = moold + yrnew = yrold + do i = 1, nday + dynew = dynew + 1 + if (dynew.gt.mday(monew)) then + dynew = dynew - mday(monew) + monew = monew + 1 + if (monew .gt. 12) then + monew = 1 + yrnew = yrnew + 1 + ! If the year changes, recompute the number of days in February + mday(2) = nfeb(yrnew) + end if + end if + end do + + else if (idt.lt.0) then + + frnew = frold - nfrac + if (frnew .lt. 0) then + frnew = frnew + ifrc + nsec = nsec + 1 + end if + + scnew = scold - nsec + if (scnew .lt. 00) then + scnew = scnew + 60 + nmin = nmin + 1 + end if + + minew = miold - nmin + if (minew .lt. 00) then + minew = minew + 60 + nhour = nhour + 1 + end if + + hrnew = hrold - nhour + if (hrnew .lt. 00) then + hrnew = hrnew + 24 + nday = nday + 1 + end if + + dynew = dyold + monew = moold + yrnew = yrold + do i = 1, nday + dynew = dynew - 1 + if (dynew.eq.0) then + monew = monew - 1 + if (monew.eq.0) then + monew = 12 + yrnew = yrnew - 1 + ! If the year changes, recompute the number of days in February + mday(2) = nfeb(yrnew) + end if + dynew = mday(monew) + end if + end do + end if + + ! Now construct the new mdate + + newlen = LEN(ndate) + + if (punct) then + + if (newlen.gt.frstart) then + write(ndate(1:scend),19) yrnew, monew, dynew, hrnew, minew, scnew + write(hfrc,'(i10)') frnew+1000000000 + ndate = ndate(1:scend)//'.'//hfrc(31-newlen:10) + + else if (newlen.eq.scend) then + write(ndate(1:scend),19) yrnew, monew, dynew, hrnew, minew, scnew + 19 format(i4,'-',i2.2,'-',i2.2,'_',i2.2,':',i2.2,':',i2.2) + + else if (newlen.eq.miend) then + write(ndate,16) yrnew, monew, dynew, hrnew, minew + 16 format(i4,'-',i2.2,'-',i2.2,'_',i2.2,':',i2.2) + + else if (newlen.eq.hrend) then + write(ndate,13) yrnew, monew, dynew, hrnew + 13 format(i4,'-',i2.2,'-',i2.2,'_',i2.2) + + else if (newlen.eq.dyend) then + write(ndate,10) yrnew, monew, dynew + 10 format(i4,'-',i2.2,'-',i2.2) + + end if + + else + + if (newlen.gt.frstart) then + write(ndate(1:scend),119) yrnew, monew, dynew, hrnew, minew, scnew + write(hfrc,'(i10)') frnew+1000000000 + ndate = ndate(1:scend)//'.'//hfrc(31-newlen:10) + + else if (newlen.eq.scend) then + write(ndate(1:scend),119) yrnew, monew, dynew, hrnew, minew, scnew + 119 format(i4,i2.2,i2.2,i2.2,i2.2,i2.2) + + else if (newlen.eq.miend) then + write(ndate,116) yrnew, monew, dynew, hrnew, minew + 116 format(i4,i2.2,i2.2,i2.2,i2.2) + + else if (newlen.eq.hrend) then + write(ndate,113) yrnew, monew, dynew, hrnew + 113 format(i4,i2.2,i2.2,i2.2) + + else if (newlen.eq.dyend) then + write(ndate,110) yrnew, monew, dynew + 110 format(i4,i2.2,i2.2) + + end if + + endif + + if (punct .and. (oldlen.ge.11) .and. (newlen.ge.11)) ndate(11:11) = sp + + end subroutine geth_newdate + + integer function nfeb(year) + ! + ! Compute the number of days in February for the given year. + ! + implicit none + integer, intent(in) :: year ! Four-digit year + + nfeb = 28 ! By default, February has 28 days ... + if (mod(year,4).eq.0) then + nfeb = 29 ! But every four years, it has 29 days ... + if (mod(year,100).eq.0) then + nfeb = 28 ! Except every 100 years, when it has 28 days ... + if (mod(year,400).eq.0) then + nfeb = 29 ! Except every 400 years, when it has 29 days ... + if (mod(year,3600).eq.0) then + nfeb = 28 ! Except every 3600 years, when it has 28 days. + endif + endif + endif + endif + end function nfeb + +end module module_reservoir_utilities diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c new file mode 100644 index 000000000..9feeff212 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c @@ -0,0 +1,35 @@ +#include +//#include +extern void* get_lp_handle(); +extern void init_lp(void* handle, float *water_elevation, float *lake_area, float *weir_elevation, float *weir_coefficient, float *weir_length, float *dam_length, float *orifice_elevation, float *orifice_coefficient, float *orifice_area, float *max_depth, int *lake_number); +//extern double run_lp(void* handle, float *previous_timestep_inflow, float *inflow, float *lateral_inflow, float *water_elevation, float *outflow, *routing_period, int *dynamic_reservoir_type, float *assimilated_value, char *assimilated_source_file[256]); +extern void run_lp(void* handle, float *previous_timestep_inflow, float *inflow, float *lateral_inflow, float *water_elevation, float *outflow, float *routing_period, int *dynamic_reservoir_type, float *assimilated_value, char *assimilated_source_file); +extern void free_lp(void* handle); +int main(int argc, char** argv) +{ + float water_elevation = 0.0; + float lake_area = 1.509490013122558594e+01; + float weir_elevation = 9.626000022888183238e+00; + float weir_coefficient = 0.4; + float weir_length = 1.000000000000000000e+01; + float dam_length = 10.0; + float orifice_elevation = 7.733333269755045869e+00; + float orifice_coefficient = 1.000000000000000056e-01; + float orifice_area = 1.0; + float max_depth = 9.960000038146972656e+00; + int lake_number = 16944276; + void* test_p = get_lp_handle(); + init_lp(test_p, &water_elevation, &lake_area, &weir_elevation, &weir_coefficient, &weir_length, &dam_length, &orifice_elevation, &orifice_coefficient, &orifice_area, &max_depth, &lake_number); + float previous_timestep_inflow = 1.0; + float inflow = 1.0; + float lateral_inflow = 0.0; + float outflow = 0.0; + float routing_period = 300.0; + int dynamic_reservoir_type = 1; + float assimilated_value = 0.0; + char assimilated_source_file[256]; + water_elevation = 9.73733330; + //double result = run_lp(test_p, &previous_timestep_inflow, &inflow, &lateral_inflow, &water_elevation, &outflow, &routing_period, &dynamic_reservoir_type, &assimilated_value, &assimilated_source_file); + run_lp(test_p, &previous_timestep_inflow, &inflow, &lateral_inflow, &water_elevation, &outflow, &routing_period, &dynamic_reservoir_type, &assimilated_value, assimilated_source_file); +//printf result; +} diff --git a/src/fortran_routing/Reservoir_Binding/kernels/readme b/src/fortran_routing/Reservoir_Binding/kernels/readme new file mode 100644 index 000000000..e3f7c6871 --- /dev/null +++ b/src/fortran_routing/Reservoir_Binding/kernels/readme @@ -0,0 +1,10 @@ +In order to run pytest for all reservoir types: + +1. Activate python virtual enviroment and pip install pytest +2. Navigate to "fortran/Reservoirs" directory +3. Type "make" +4. Navigate back to "kernels" directory +5. Type "python setup.py install" +6. Type "pytest test_compute_kernel.py" + +Six tests should run and pass successfully diff --git a/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2010-10-01_06:00:00.15min.usgsTimeSlice.ncdf b/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2010-10-01_06:00:00.15min.usgsTimeSlice.ncdf new file mode 100644 index 0000000000000000000000000000000000000000..cc823536e33fc147258fc7b46dddf5633b9264d3 GIT binary patch literal 1156 zcmZ`%!EVz)5Otd7kWf@gk&uv(79nv!vb&qaZ4#-6DiO32sSrsHfWUI>b+XdhZoO+z zP!Alr@GD$7apM#C11?B#;1ig!oe)AY+R-GlZ)V;;Pq+7|WEjRacn!P^zN@sWMI4>^ z1D!nK5u}V=@SR2eEb=i4R?{Nne__TxM5!(wg0)-B zMyPQOyaHZb=!4mu6Kacwn&9uMjH8JYxgp1Fj=4V?JRXpumRB=nNrd^i4A>;p@gh!C~Fpd%bx-%S*??b3zVZM>()8j^bWCUog}acB%XW z;@*OTbD6qQ=ouuzmbu{muD-uyX0Wx|A1ru~q9>t5$Ar-=j5$%BE4fd`Gt61osTQVE z2d<~%Bx{U1l`YEf56(xvzaUmGQ=ZIlhw$cKp2&HX=fnKriYO6zoUry0t|c6jttD(G zkuk6#!xveyQ8d literal 0 HcmV?d00001 diff --git a/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2019-08-18_00.60min.CCHC1.RFCTimeSeries.ncdf b/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/2019-08-18_00.60min.CCHC1.RFCTimeSeries.ncdf new file mode 100644 index 0000000000000000000000000000000000000000..4e29f58c958b22524c0a4586fc840dba901c2279 GIT binary patch literal 47863 zcmeI*4R{kp{s-_$(?VNX-rtJ6qzEENOGB+%&O%FHY){HdTjWr&_9ca!Z30b-7C~MV z5f4PXL$4?*C@3l(ps1*bsHk|LqTq=qA}XG!ps0wb=*{fzg!F5_Q0^)JmwU1g)7_o@ z>`Q(-*_}*w!ra`foY?3t(P5F1LJz-M!;z=WoFr?(b@yMjdqi$lL0IfzTUbvc)85ir z_G2q^VqR-=yw(~=hY6d?X>H7>+0CT|=19a`rY7v7rHB;aLKF|r99iw1=&JU+T|UuH zF1kRbFqtO1s(o&+C(Sw4#J1@Tt07QjbFZ`e7C2@Z@pS|5GtLQ z6myo40kMl%bG<6J&*%1xzoewH#$_C`43;4iQd3ivjgYprX+5s1T|R$tg}2&OR^sy; zT%7{7;iQ=ULNt@MooR=U$uG9zSnR2(Ds@%Y+xp~ziB8D_L+`C$tT79Ku-V$H>@b_n z7GW*dV|#mb%G<>EP^VN54$&!6beb-wB8hA&FU%V;ytptkV_3FT?iN_(Zh~4{4rXcG(a)Tl zVvdC9qbC|M9=QFf*b$kz7wcngPLEmiY{cdyS^mu}52`K3Txyot2h0kut?@fNUjOk$lS``IC8d?FM28+y#P*c;)|+Q> zK+suKZvr*w@u{E1Vde%--&q`Kl`wr4V=7fZ?4p%O60!&tWanfTWDn2GE*_RwSR~Gq zu~MZ%zS~QDF24|+)7~aaN3+37Y@BSe2N=rSEn%k(L~4^MBQP7*Ow%?oYQ zl?C-yQ-dBajy30X**Iw3`tAlQxfus2CZBQ@LZ>E!;y_3KH!kX^%nRmRo(dJR= zE2y|oAN7Aae(T^Z({l<7v&Fksh0m5P#51Y{e>Q7_te7-PfZJ1aZxk2F{)lF2fd2EV4k4jlCHd6M4LDDO0Q>pv8SZU ztj)#CMd~tBkdx`~`Abx_(BUq3dHn7QSGB`i;iz_&3dvr zI<(ngYLL(8 zke>r$y49<5)p)G9s(PbZO_9shq^fPSR<(`e6-}eI5hsy)2k1(M3W8cu4SKAiu){ht zYQw?v>dL)#`o!^aWDS9W`kSc6TjA7E)mI$zR1UXJ}1S` z*~g?CImThAO)1cbC>v{$D96i9s8^fi_LWU2sUGi2EUPJXmlapJ{PF`_vHWCL>~ocQ zJ>@YDQjpaM^y)sQ9RQq8aT-eL*-!&CfJLeyoLg zY!~T6G-EC28WB&gT8YGSq+Fz;F=m3xKS5p!ds`?WEbwv_Dy=HPy4 z^(;d*Q%W^CNM&6ij;lp!(?{eK%54e#D$G6Vb7*Z$j>5%AagA`uDN|%w_ebkZD)e}T z&og~!(Xbp5@ng$h;>3M&g{~qtv#3DKlarl9&W)`u5g*9OZX#yc^l_qEPIeI=dd5!_ z9pq$BQGI#8>&5v7nE2qWw}^3ae_h2{WfK>RjWV}~_-gPQ_lgT;ZoHU(`=m!jk<9Hb z2K+GaNl`0v&l1gScRnY6lDTJ!f-Bp-EV|3w?&7u!QxA%JjE(J?zC$hz4W}yt#`!3tdbO{3z+4`zo4CJn+Xjki9q1qA#eWjV%9)w!SQ>ASq@r@ z2%{=#@2BQ%Gj#3IYgKJ@X=%hwLKQD|ljy7tN2W}#thT7yqHg*#ZxFtdSK`dO*x3GR zCqb1)>+);n2L3i1fA8x}7mG=!9*ZIBP{<9_As4Aw6nXt6l@4#I&s9CyRc-~sQR-5m zaTFybHZ~@YVN3#VJh>Agj`i)Hcq*|luK*5V)yev)d3gz$VV;0j5{!)+cMmnyh;lJP zIT8zXZ&k6#sdi1Oae2yW2l!o86TQ_kFx7$P=Z4BlPjygs-Nt(SLxx#d5W=j&`9%F# zeCYJ_GGo#HL~R;@`jk2}RcEgM9x)>kOCoWC2C3m{Ya|k?Q9@~6%l(sHE+gD1g<2t*1JdF9dwB~LBoMjC2HX0_Omu`om_|fzM;(J zaHgK0l#t|<{~Y=&4M(L{M#)ihsd=i}so=^X@KLjeIe)^xcW19jGd}PSGvn9z&s6$` z8$8yZsZ?G;pPkQNlsRIUvD~WHYOAB+5{I9ew-P`FcCL9)S6}LV)?Y){Xh9-mM1A zH*bLJAQXX61VRxAMIaP`Py|8|2t^%FWka`qs8)rP?>e+l#}N3Acfs$?qf=0) zracVRI1zjpDk>{psO!`8%{!`Dqaf=wZ_{sCHH^$(`_eN;=&VOPu7-x`7}->%p5$$s zrYhA!vS{_vU*)JthTtRjSx2s$tJIO7+Jg(~e5xUMtc#gyEUJESS&jkzEC1;7rP{!o z=4`75v-xulUSn+eeIXmpoARMm-3+J4`k_^Qa`c0XdR{-csOS5suQG#%^Ew92$jPDV znUBsktJ-^k z)tqVz*X_1ZfuqsabX&M?yN!`g-q+~f0y?s`aNUF(9e5hUr^9Ru*C(QN;JJ)q<53Nt zj=U{gH|e(Ut1qgIZ|;2hJlMkZou#Gz0?P#Y#Mr`h3vZMeCD%lsK1sH4bum*zyTG9p zo8NZGkmyrq3)gMFQSz&y>RQz$0Z*g3mC)Z%1VRxAMIaP`Py|jP0-2e)na-g8bo$HV zcw3TaEi1p}Vn&xAjHl_hZQYu-ZR@bXi|gtJ%jv;#Cqow3#SKYLP97rjhg4Kl$Ok@V z$mw}9zfR7tkjoyEdCw2of9%%^FEe&pUS-bGVe>7_qELXR_1*# z^Nz~ApJkrNSX^hzNKUqA{HMqL-@aZ&MUITsFd4f-8Mm=AW>?A>R>-_cndg&vwK8vp zoW5S>-zewj%4PFp-mU5X?Xmg4_WF*5j~;p?e3VT2GUdsXEmMX}X)>kClp>Q;rUaS# zS}9(RyU7$MQwN#a$P_D6luQva*@k`<^<&0YQQye)xlA9)^qx#_$@H2`FUa(?OlxI& zNTy{nEtcsvnQoS8R>lYI{4$lxR3uZDOy|nfD`QW)_A-TMZ0z${`o=!5%JiU2H_5US z8bT2WMWE>-P*jket$K#*t|@w~KZ&zF{d$GD1YIhu@QQlCS`O&cgVw>r`EGL#-NV61 zPJ7M1CcoLy0DsYS>}Dan6c{9o8E+t~Qje81+VxDs=lHYxl$Ypv%g@cDq5aJ2!E^16M`dh2SO;mj7@)6K_!y4l9>nS1D5jPy%dg4Za+d$k%aGQu51#UBO z1>m+2R|sw!an{!j0ll5L(E(vj>?H0IaJz^b18z5Qe*m|axUt~&5f|vqZ~6ViT?X4b zK%D&Tw0grK;*3W^g5+W1tgndz@+fh|0bx!YBkoFYHtPm}_k|L05yX{(iz2QJTr6?r z;Mx%90@s1K3UG16jR)6_xC!9miL<_r3+Ud&{V^cSiN3^L1ulWON^nl%s=%cX=K+^W zoEKagaTCF15H|^2HgVXV1*?T-tq26_jn)^`?0aPl*i7hSp_l9SJUu^~IQ=BMc5{i- z&$MedpE$fuEhJ7q@~*eHh&cWDyLNTN>F40JTSA_!^Cw5ca*pu;EoaJ0B0lhsCaM@#PtLhMckR-Vu|Yot_^X$!F3?+EO2qe z^#Rw7xU<2<6W149Z{qra>r31@;1Y=I56($k0=N|762YYsHvn83aY^7Zh?BpwR&U5A zZeTqnL>_U;;PQz(7u+b~QoxNS&iEg<98v0+&JQ_w{<|45NBlvTxg=<2`+*ELrh=a#GMGbM+!S-s2n+|RoaWlZpByJ|S*~HBPH0Jnj-TfuE2 zZXvkM#N7sN3vqu2w~e^JfZI;oB5*s2yB*vv;_d*qo4CJ%+e=&>xP8Pe2DhKMJHZ_w z?k;eLh+6{gFmZQ-J4)O=;EoY@FF4!p%nWSbSPCwJxMkp?h`SG5EOGaPYeU>}a2<$S z0WOZX2f%eB?m=+z#H|F^o4AL-^(F3MaG}|TBcSVz`ul0!p9IS{#xsG${SnxWleksj zQiyvLTq<#Y1D8hJYH%6EtpS%!+*)vX#61QspSZ`tjUsLxxY5Kt0d6dDPl6jq+*9C6 zh+7ZNMcmWi+{8Ttu8O#4!A&A=1GpOEo)+(vNIh(d^ts!n3xOK$64sJbh zZ-CoC+?(Jw5w{)OX5!ugw}rU3!EGaM2e|FT?F6@zxOc$qBJLmHb`$q5xV^;f0=JL2 z_rUEZ?w{Ze5cfW~L&WU{cbK>jz#S!S54dB*eF)BWp_zg8jlJL^i2Dd!6mcJeizV(8 zaG}|TBVg7W(*pgJS$}GbXB!gteXyGj#C-}bj<|n;>qgvX;Npqf53Vy zi2D+pleh!mQiwYUE|s{iz@-uQHMk7o4uQ)i?i+A<#C;1cpSbV9jUw(axY5LY4{j`R zN5G9E?gwxs#2p3aBJM|UZsL9dS4G^<;3g4w3|tLyzksVH?pJWrhztBAsO4u87x*7w zEjOFEFkAf{&n3Wajn2@Ag(pIO~kbUx0$%M;IxV^;1f!jx17jXNDI|JMS;<|!6L|iv;hlx}FJ!bH5l(-(?juGboXG;s- zdi`HaR!gv+U0W=B%Ley*;&Q-UOx#7_h7p$w zE}ytOaKnlFJ-89XT?}p{al^okA}$|X0dd2@6%scBToG|2!Hp(v6u3)>D*!i!xI%D$ zAg&19SmH*5yOg*~z+Fb%7;u*p_Xlv}h#L#;3gRvWS4`Yx;I1U@a&RTYjRRLo+!f%; zh${wHPTZB?T*Q@tt01lv+<4;3z)c{o9Gsgt7q~wXR{`!S;>LrkByIw@D&pMWJjDGG zoR_$(z)d8s65J%>s=!qf=K<#<&I`^@+(d9S#7zP>nYe0jq1lEbpzDpgoss=+4z3+R z;*RymX2gZT2AdOS2Ny{k*1w~Oi-7G#6W0t}3*wrCiy;93$F4Xzb&Ex@%V zE(Tm1;$p$IC9Wm7cEq&;*Pgi6;5rc323$wt+JfsuTsv@`iE9roj<^osx)9e9+!@4m z0@sze&fvNc7YD98ab3XmAnpus4&u6kizludxSqsy2X`iMJ;3!M&H=7Baq-~JBCaR6 zKE#~~?rh?Ef$K|LZ*cvHI}6-7#PtE!pSZKZB@ovaTq1G(zzrbo9B@g*^#|u9E&<#? z;u66n6E^_dxx^)bOCin)?mXfKf;*qMWN;S{cP_Y8;!?m3BJMnJza#E^a2FDH0k|~c zuzd_x3(Z;)(Dg=LUu3_Ji1qNRN!)!9*=vaNgR3R32HX_lCWE_{xU0cUCGHw<(}=4D zcO7w4z)dIaT5vOnn+k3wanr!fBJMhH*Aq7#+zrId05_Ysnc!|DZWg#X#9a^WCgN@Y zH6|Bw-DUz#N7t&4&wd{?ytoC1za6*i@+@=?sjl@5_boc#q?p|=qiCYS81#!#3JwV)j;2tFIesC*^ zTMq6a;#Porn79YPJwn`r;8qd065ON2Jp}G=#61iyG}~|lbiGl3KdpZ+8`}*ro~udR zAA#MhA#N48wZuIN?lI#22JUgN;R?g?&-gL{s+r@?I`?ip~;6Zb5*O~h>g_X2UxfqRj-jo@A)?s;&ViQ5G3 zW#V1{_X=?@f_s&?m%wcyZZo*ohZ-aZ6xEy$fzPal62MK-_!a_7L|^a32!)KDfQa?FRP|aUX#Dn7BRQJ|XTyaQlec3+_|m zJ_7eI;ywoV8F8P03(Ynh0khtiCUiTa{=ORPjTq1UB<}lQH=h&tDY!3)`xm$`iTe!P z0pj+9J4oE;;Jza63vgc(_a(SP#2o~|^m zCYCtt7t@kB>=)CDIP4eGnmFti(}p=)CHIP4eGo;d6m(}6hb7t@hA>=)CC zIP4eGnK=)C6IP4eWAP)P*#1n`8 jVtNvX{bJ4}4*SLQA`bh-^tPP7eCq2tj`i~Y>G8h-%F}KX literal 0 HcmV?d00001 diff --git a/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/reservoir_index_short_range.nc b/src/fortran_routing/Reservoir_Binding/kernels/reservoir_testing_files/reservoir_index_short_range.nc new file mode 100755 index 0000000000000000000000000000000000000000..2bcde0010dc456c151edb5a7aebdc5c867dc0dff GIT binary patch literal 114312 zcmeFZXINFq7A-o8AZ$R1l2t?m#6VCG(UzPMQ9ul2$ypEs<_xHaIp=`d227}ED<*7HB!H|K#Y{oZvsT|4TbuO;Y&t zP+RK@q225v#IFupuG!oITDY`E^AW0c?C>))U;r(7XiXtPR=(LHB_;LKQHAG03ZvwH zI^)$Z_OF6v{%c7}7R^wBRzG{r*~ih}pY7%zYd*0*LzBCB4siAL_Z;XO=I`w2;|itl zW}ATQPcL7`0j>o7^l~6?@d}8G&oJQs5eDpMXBRhvsQBdQl;*BCh|C;qkdiw3f11Wb zq{l}@CPnu!`1xnu+qNeo_1Zgb!moZ}2mSB+NvS!liah?+PqIHX&HdAS&S;z}^RvfV zTJmbmN@)@PGcFK3oG|{ioMfs0-e=~`I01#9N1v3M5*wBhksRHuTB&PuPqtj2 znJMw3GiYhgVPw`v2|kV5eC--8?*<&~l4u z1}%T{3%b1V7yr-CueY{b|McjL==3qE@#$fs$EQVCNdH2vx;%E|KxYqcrLE2F|J@}r z>8Jgdmvd4?Mn-%L%OBnR;Yf`!_*ZmFNzoA*(FRcwqazHOQ}s3Qi5?T3WRRAcnq=^s zfn_&?K!11t?!4V)#Al3-P8k_ZtyQzu(b@Gs)IGZy40dzw9+R3LJu)I=bOw>VmX*YP z`dPByubx)sCtv&DKJh-D{sDjvD{a1lwMhJs{p)_!at8jLhc^S7F{DHN+NAxA9{wx( z|Ft`YK~Gx?EQp%X>)*5epB+o{N2vL)g*VsVaxBf4<-fOEvAsEdBz62y15Q^^>ua)`*p@N z8KU`&HD5Rx(W9F~{BQqS&g{Q#(to4>KAHd3_uuci&7hWVvX)+K;TbK$-;r8AGA)5E zZ|mHEyYoPwpWV`2IsaO^sHF(+zZEU9{!{<=$MEMR+U&1=srldkdGBk%x9I=A+x##1 z=F{8!*MfJr{Hy)|BRo1^_RZ+rq=w|5~oG4 z^h^CO{gG?G6s`Y5--2)RU-ZiVMgMnq|0{k=ovQzVpLye#;^!4fv8B!yeamgOMPG9F z7hdDP#AzAewcuOg{5=Z#U*b1kq|JZ-iT|Yq%3C_5Mez^+rw9J&fq#17pC0(92ma}S ze|q4b9{8sR{^@~#df=ZP`2S@ONKd^3nUZ)RJJk@a9Q1|U)M1b>nG6-`)bK%~ff{Hz z=t8H&MChU)+Bu{O*^)SPq^>jV##jK;sdDH_xm$@9EO8Z9n9{o^?Y+?fwk4kUZEB;$ z-XTR2-yym^qKKFHX|;oI?XjOWZ{neJen~#1!?Dw%k>FU4#8XX@WY(2RN;{<^Mlr?&`n){91XHKE!J*m#GaL_K_OwHoGc5|a ztYQAC6UY~xuxMI)ESvTmYaQ#bj@b3oZ=}4H$6LUfcTa1S?4f=yYd$D^a9D)k2;1*- zHBoX(h*QjYmNhgnmru2c+hUtzwF=S^|lQ2|dwFG8D`O6sc|^>NS9 zU(^T_@yPKm9*YV*WzMe&D^bTet5=RvqLH!Q(*BP86YXCeqlAo;0_2?Pp)kD$dkf=* zvXqHXm8yU`C%81Ee9%@Z2wEg<;&hz!(Q&%5FqW!;330*f2eZ?j=sNulLNEswjA3~? z2R)sXU`=~(QlHbxaHh^p$`)S4dei1Z-g<5{1~Yye?+=opQhH+ebT^#*ZiHA~UkOvv zMW)jxOg>$R8B&)pOR6*GOpnIA(_O@T`Y)aCiRGjf%(v#W3f4=t$EN8PVw+Q2@%!l} z$swsZaC~Hs;_135aT<$L#GX0rhcW!@mPrj2*H7Dv8>d&}!23+poc<`ObxOi>r{|bx zU@Gc}{lH`Ya@rMBH(WuhGoFw;vl@ysJfU>PT_~UNgBo=@&h_XZ9WFXMC&8SyY@Uan zGi1?ghPtpm^9X$?J4tK9O?n(Wh#xc~8bio~h<|dk0>RG3h+@2G#)vsnjd;eW4g4TU zavq2j;?mBjU<~E4(rIEM`6SwxPVFhCI(NtP8T&Dlv2&@PJ3|%oo%O_u@PSxO%vNa= zY&)|C1!uglbB2caT{;A7*{AzxJ4pPYGf9#ojK7|r{Xe8lL=$tCvW|`XWLMC4(^L)I zU`=;fXC-TR%DP_=S0_C}G@j{)*Uol$<9r1lojvhwMuzyo`rz^c;%otA&bkZvnHEC9 zWuQ=;$^MejMw^+vh3d==$=)9o{XbtCu1TE&U(VgMMW69*uYd~2D-bfhPjJA zEL@Jj%H=w2WEP;WOq#GK)`7Ci*|%^b-h=XC-e-L1KhVVvgBUx2aRXhFFyd@EM$R-A zV@a=PkHI9Fa50(q5}unJ84b*HX)8`|FcEn(Lq+~f4=keJGV&EJo>()}RIFu;4Kl`J zBmFip{ucUfo7n*cXHTFkTmidXtVPAt0vwS!hJBMmaCT;xI5)FVQc5Z(ooBp@)LovL zi7WKE$v(M5-Cf%5$*ji%%3r#_lRRR(>1#m52UNa4EnfsgPrVF^qr-LezaSU`7UvA^@J1S*o-NLo2(H$U6bG=8wcAV zrtqg6;5raN^baO}@2UV1%61rIg_B3n9_Kn939~{)I(^2_fBY;@OqA8bBx0tV8;5Dc z%p_*^IZfos-oac~Ju#ohu)tLo`Lab=O3VtzTIHH3{wf)UHM7*kCg$4ATDS5zcAWFa zZpPX}O#dm#C}fTz*D#zo=PsIf?Eq;%9&>lx!!t4%6Z&ibFr6-{gF%Ywe znd4@J`K9NOJjM|VTh(J}tK(SC_Db?~%)NfLDz-33LFoeQrv4!Fw%>RT$A~@QR*aLa zN^pv`mJ(xFV<@ihGjey4sOW5h(8LYUT=$h~3hO~ia+ z4WHe@#TWL0blEw`$o&D?vN&}8Q3pl2dMLTuL#50UZRh9+EjbV9P*>@F0UgPW0n^S@Z7skMLj)}07lc2xcJlMN0goB)oaA7_7qN4?Ad!?J7l70`cQ9J62ARZ-W!wqw9+=8_4iTo9MU11Uz}z_>Ft6-9 z^69_CT@B0Kr(hQEi<{)qv4u5nn`0{6d43PNyNSchb!5&o94j-!ak(O#nA2S}@q4O_ z`f|D7P%%eQTyi%N10v4%zDC{kvU=Q@GX-}T`w{U^2MUwMK;uaPOT4(>0S5BN&@p>FMohZ{Bae6JoUH~Ed2P`p zTNBpVH|&j=|Ym(WX8LDoWi& zZ1yR{$?J=_8b>U!b`ayUV=$pS2U9#gN~TesPM*jv&3PqLm<?Ci!^c9FlY~ z-SZu0c=p3A>av+@9&2e>R*Z!bH!P~~6DxBPfp{OWg}9wL>f(3FSnMWtUxhyodPayj z{Yr6)HJp~Dh%-4!IG5vz3yi&KV;HVf$l-cL5N=j%$1T=#n>F0?Jc;}C-^#IN4RbuM z=q{czR&7o+o=fgP;r4kHoGL&=PA%Ru?pN0P&2tidFt)7M6v!!zh2*@sP(1G~lyVy- zD!Clf#&cNJy_%X&{>H@jQ7^>iiaQdq(w@2K0E$kSqhb8RiMB-C(ImY0Z*C+)q zOz^scT>8xQ`XI^kdWXfV>nz9N)w!l(9pz2b7vu(t9l7q5FAcp=&F71&HEQDN z`Hzw-9lSWs|0t;=P3G9(6|rxa<2~C=3hl%P#{0;zIp&&*R^DSEncE2pgT;~1!8t;YINjFHhy%*DH)*$$x^8dc0MY?k)L~ydFjIY;zvmc{i7(y9QH&k z^+`&$NV)h8>P~UcnC^jT7Yi_BUVqG-*C@%pcph`--NypPUP#|1^Q6Q|@>TQB!ZGY3 z)>AH>XNhg|mSHDj>}7jDeGd2pp_p<9!$G3JC|sPlD8wnoEmhJ)$J73}Fz*yDUW~^j zA9q})|J8Y0aQ&hq9x~6Inzy)K-yg52e@%OnQdjYbvA$l6f|-;Lb}vg33YR`g6k8{V z)~!p?X8s$A3h54g)X3EbG)gpSH|KLsJMvjtUN9IC0^>{PVKP7oR;`cmIZOu+lJ22* z>o(}u`VM}Z9}Oqky~%xBYvbeSRtUJ1D2C8Bl>EuO?g%H3r0>W}s}M859trbZFsgM7 zGU+q+Qid2mAPG|koWKm`n8{dkE`^Eg`5s~}+xe{nu!uV6)1G2k>vdR8-Rk-ESWCWP zexqc|r8U@gNfrfcZzpc&rGD7O+H< z|KWMoBF;A(CdD6+K%R8@29htAB8Bp(%NZggFB+qL`TTd;TZ|(g-^NvBwF$$NyfTyo z)nGbxvwXi}Hfx_lp3RuKd9GNTmx$%06|}FW@9kUOSj$)&eAUGk-!3A3Q!xsC^RS=w z9caU6WM3$px}1U1yb@fzoq+R~`3yt(vhPV;8ha7fF5kv=VsEj1k1=Z~Kg#pNle~7~ z*=0RZX;y(3zSHEo*~a2cn+15wT!VVNlbi@NfNY;Y@rC&Bc`qTOdt`>L|PNM+3Qfq58B{1{~`F`fBx7$bM! zQOr~3c!720FDS%f>Xr^H!bas1?0O|7wlU6j<#h4;z##0R@16x7qLA$ato6`>E-0ez z2&tI3-=b_s2qcaU#8H^uYkTv zx=5gGK-tJI55|6+_hkQ@s~m>~+mD7aq@ zfd}oLBrmr8NQ3j`L_q#g1QI`lenD5wF$ZTxPx|#xu_Z(lq9uN&i_Y79yLt`Ha25Zy^>Fv&?TF zmNWi_{COyiY~=lGJa*(4VK40mXg^H(2#^10ei=?ucP2j%W%;SN#`aymPU0bB*W?F^ zN5nj3?e$k<@Ry33c*FKv;+w9zi|>?WRF9$6HFweGS`1W3s)GX1cF;m-Qq~@H5;~Oi z7M8()n2zK|*Pg>vbqKmp--9}fK~f^SJ76^^8V?Lx!G^lt*UZqDaegDFziL;wha@4_ zxDR{=1&aZyiLgCV2mfoyV#J_W1T$X9AU%X%tHa1agV1lNGZGgT<2UU}D9VfwqplTT zoT?_WR9!G(P=Ki8yw=QXj#$>+A9Dt^g3}Rqu~1b_ELQawE17RK@oTP~#X40}Y+}r< zjJ1>U@2YMnWSo7ffjGD@K^#(jilfw>pnQt_?4V3hN_-jdK6gBEage{bM9fXrHiqMr zhYRn+@6KF2rG1dCkNCF9lK1~md|R#Y_kdPhh0nteY@{}cNF+y?h45(74 zGdNJ_sy)QshS_L;T?Gb|jYy`~bwszp?_hqt09J!-&{u6V`dxQ`{q<5fsqKa0W<}vb zoGZti?rMSX9V{@AvfuR!7^K!k_%HHA==GJ59iN26>uMM?_$m_EhjY~OkUiL6%p*RZ zwCH*RmNLgO$}1Q7h&5{6uuiQO>#rw^T;8_}7<o;(WF;9@6 zVxH4#x}ua+uBL~JjB}m!-dxlN_i1~?_>a}f@N)1_yi%KjM%v#H`~LcOd>T9nUnxVq z8)W^<&`Ny@ZgUPvP5mL-E-r@l4O8eYHh{i*9NI08#w>nrs~XB+?C&p3Y3riC17_rx zIX`H#cpB{J*N<%nbzR}Scm~|q_E4{d7rD=kftauDg~9%+@Lz0*;r?4Of;^b|&>QOz zvDg!lH%vs7`dUP*4;B43UqHfQHIc-4qts)NPClNPX`~tc5HXgXm`(f~buHA-4?`|% zTH-$xD~MaI9))#_Pom&P0d_840`bQmD5QQLMdRlW|oeA=yxDjxrRe{;^Mg>Zh=6VGqN;066&lGpiXqJee%Mf*p8d-3T;DL(r<@tAzK$0KzLo})}g1*7bQ#@21;TlPZ4Dc5*e-Fm<^^a_ zj@%!DyLU|FbZ_biJ+}3i7Q%vKtEwXp(3x%Hn<3)fk-O*>I70LZ41gWm{Q~`k{Y^hO z1UjQeuM#fAxl!ju?i;89KXO}Nd*qEAA%;;NP71zRjPRRXxJK6n(QO07Lh0w|mp2b- zfgG>VJ|?hHGB!{P<0(%dOQFhaG;OlGDA4g=0=+bv61{d1yCQwF=H^> zA&eh2#1t`{+m5|ujX3J!HF>@FF~#UxT`-n$CJecV8Jd+y&NIfMTkcqPD+uuzFL3wDmyciuw25cJz^@s8D zqp&<0Cb|YqK=+`Fuvo5T+l%9U8!cVYhq3G^_X~1HF2~jV$(@(4h0R?PIZxgP&}Ae1 z=s!3}4FOsa7#egPq0Ae`XZrj--w??hBN-!_abt;3xt%G}w2VbM<78;5im{wi8PEEr z1TB%D+Wd@hyC>>qq+u58%OQR~DerbM7SZ;h$22StvKFfuYwhx2(Kv(aZ;Z2@KD$_J z;qvR)$9b89w$^+lpCmPBsQGav;VLF@tMyTKQ?z3-_N{vYDn4PE^LSo>r;w;^i|;cj{S-XI@6&*x*AYm}kQG7r!ROK9HgULdz*dA2A3=(|qk@sVheh3yh})fIMFR23+IdJJ zA2-w#Swq)i68)!8H}y`Yn0coYX00&9oaPz~bj9gcI>@_|jfE@TW6_E)lBFy9V+H-r zrWj!@k7dIhX|Z+1cob0H#XNiN0PW6xz(H+26s;I64pUc5Iz~D^R7sroEyanUCUOQ` zZ#%911ZR2QF29p5F0qEo+7fYvxT|-*;X1z)Z?fNRX-|exlo2Yazqg_X?vpYG^K4{{7sht%C~kRi$PI&MXhr>w}i=Qek3@SvLO(K=k` z9_Ea8)OFP9COQp+Lg!&gFkNYjE-NEocGp~VrLD&>IauAz#?S>z_?hs>Z^YX3x^P%o zD{*Grc{-znFS*~!--Q3l8Vnis4ng#Ll4mAH68DVH?oZktMG7&gD~<7-&(!IZGf0`F zag3ieEDZKrVlk1ivRUWyK1rB&S5+*z>xq?jM~GF#t<}*H>vRIJ;jX3FxH3>|x*H<4 zt_&92h%cbsopVLIbab(nISX|R#KDypaA;*T4zCOlM;P}wZGR9~Lb;T-jkHzJ_F+$e zxTMoYT&Df%-MhHSJhyc!ahHAZfH|s%aSmstD{5Ch!(a4yJ?tCa-c7^%W?#0yt}Mhi z#{EvhDqH-n^AwUw6FG(9i9)H;AFYQ62xXEQbsDQKAU7redyf`DpK>SaOt=Qvjl2iR za#b{XlH2I&Lc-_eK9qkW*^?Y~HHE8gC46Zcv?>At%(nU#}b~ABXhwDLK*;f3{_O8k$*iE0k)E6@M{>n6QKvxR~hmS{5r70>trHNy#=lH51 zTwvc^stgcUR`tbgT~`d@=lKDT{~>cc9zGM#DZi`?5q0$cYn8Nk&w83FgG86qt5D2L z!dKmOkmEIvsxr=3qzE~VY2-O?s<=8ptgiBg>gs4d18bt~2t8<3aonQE^+odbs|O1s z`WRP5!-O_7JtK5mT`TEMym?iCuwaay#K{I|qkokr94R}ITd`8&A zkJvt(R~cG$9>W=Tgq{{chzZlv6tQ|wYuMh%TA?*kBayh=%#xce_PAbt`hcm39l$fH{)hOpWzy;ziju%thuUUg^)|ErsJYw_67m)JxrNj#QuUW%+ zJpC?WL-1j2VtX@fTl7P*mApVdMQkVENqwQdirA;0i~Yg9aG1K{V0CefaZge{eeXQZ z26G=5^PPJ?N?f4s66Gtw&yd6K{!X8bU}R?_YStLy5q%!h{v_C6JS9JT^)a3^e%-w- zsAv0nl)x*t|Dybs^qzh2WsL;inEOZYL?Gla#C>nbtZkCC3Rw%ewO=F(_f6y!Nv+!j z2o>s8sZ(cLBcvy^sMB9-E84N$VXY$!$vcJQpbL3dl4ZMGSgqw}AfzK~$?e)LK|kv4 z>E}T1!nP~pxlwi}_aOHS2^77DtBL`XecSbb-~IC#OzzLR0z;l*DD}alkhQgvFt+nK z*BH6>9b(ruN+ztA#oUq45ILb3>C88V@v}mzVbA;5#E^PSB7R0lfSAJ?oj+TOC5*A0 zw1Tvfw2HL0T{p3gd_8Fc<8BJ!7>W6|Q{GA4UdjjV7vdn>Pad$3S#L3ONpnnmg1OF8 z-)5$UxWGA}i|vl$(%Kwcq0iN|QMg9_C&`!hqr3MbQMEQu+$XQ5?J;9MZRalf^ZB)b zn71K%XbKsMPwg~>ukKNNqwWXmmtM#HC80G?U3VC2>q4QiPF1w!o-!?xZm7TL^uQbY z%%jkS?d~KCVtbMLuG@;=*7Xv4JA#DEx-PskEnsI%5>`jeTH-aqW-d zB}3OMvJB7%R>9WlWUP_Sw+~{F9&*^ zxmL{9qa$S_+B%br zJLqC6_W_wcv=C-tx}s|s_fmwZ!(zQH_xxzUnwVvSb}whb6?V zpuCdDxsSOMXcvAc`2`#cbuy~MvN)v4om2N z`k@}q(El9e(uWT4**aKU=-`1%>jTAQ)^&yYtJH6wAYk0E1(YGSCvz;TIv#`80(MwGt_nhfp<05o+9b z+m?FG@O#iA*QQN3JYDF8$KkJ%CumP!1MXQet~N!mY_c#9KMO0$*5Tf;2~QBc!z0iq zd$$xN7Y(#jV2&P{s?GYO^ zMI`Z28s^=Gk;3B*if-l^10=QG4i@@(oCk*}cdO8Tw| z*Th=#O{`}N<8Ni`?X>Tx&cV*`J4koU5PR4@K+M7L!#K3TS{z}FV(N}jE@AG|l+T1O z#5rQh$jce)B6XK2UnalKe!9zADjEMC+YdH`qPqGBYN&g}_G9wKATvB8mzy4idg>Y& z>veb%-mp*KF>e!f9~kd5V@%4Xax=0@ACUuXQPa0M{3VK9|j~tk`eto)7O~Xl4}K~#CM_GJ%abdW*y0j z^8t3`4vgcpQA*5Z9UhduD0|mTzyM->*&a+960saX8)Gq)axm>7%ooa-;Sr|D=YF{; z=8UCJ9C8OjP8+(am5y@D={ShlSmSPS4H$~jT=7@#ZLZ9u_@7Op*?4rDTV;1&O_Tbu%XMQD) zGWK!qD>_MjhIBTmS?Wl@p#6PF}wKA3M?qV?OZDg#29G5rMaDNEvBCyKk^>hMaG~#+>=H7+OWT}Zyf2a*pV1(Al5hBJNyb4D^=4A;A2 zDW_AHu_+d#xt^N&r~s2Fx8+!U+NMXCP1~GDlaWjOT;^S|N#1rHal5GBPdd2C8;2f6 zh$7}MCgwPE{_)5UC6Purvx#ddo6g}P^^pnDxJ&;^9{c@Gf8oKSlbDc}E{r+Weg5b? z>I^GUPv3@3isCP>sWfg1N0VUy{_M=}c*7$6h~yxRbFrdR5#%~4La|dGlsKoU!g+h` zPPH)DoQWha$E(G zNBx?|F?@FEEY?_*#HShAVziMbCT;m4nQCMzrZMhx=1$~uROJH)%ri2@f-Q;A=AO(mTti$* z-%U|IVq4T@6l{5f9Y&qRE@E~YX<~m=H4e~zknxUgiN%R0$>Q{rFj2-@%AdrGi(Bkr z7&As(*|G=Mqg-)^?YqQP(dV8~FOkJ(!iQS|M9q_M@n}mAJf;36d0mvMXkcB9jQ945 zA>OmyWE6}KTgvdoD8u$E={sv^6q4MRygse9p6vevP>kq~XD~ zck}?^^E6ou+8T?1t!g50>tzIYo)0_YqX?sKcxO-K^ZByfw=6_&y@uH4d9i4Jkr@39 zNu7sb6x*3wZ6vdq&yjmtb6Lw=+VZy^!*bTXf}er)#B7XaKU24v_o%H;r9}bxcGAwL z5WC2C)4qqiFgglFq$BiuSLlh8^gXvV5v83M!e_R+xDY)LS9wp}JRw#5**O@uqK$BS zt3IE3lf^yyR7Z1f1nYj(xtDm>`2?O*SO4?~20!bM*X)Bgw7-r16Yryov15W6K0OT= z@;ryTc&-$9%&nj4h@q{DplO^8?PqpEhHJumCwQXcwg6!mvlvD(MrgIrUYHTvHKrKm z&)BcV0dUx61(%oz;l^0rF??QOKHnG(4Bn=J0NREaFT_y#M$f(vl|kze$@a)?iHO;@ zg6q{Y5XX4&F@{KBJJr}4Y1=L!J?0!HJd?s?V}CJ=`Ewa_VazKmVZ7zWUB$|0PFT(M z8enruX$+99GJeSa%Z5Y}Ah*5LDx zJAJ%Nj={Uu559~uztjx#?%l-@lVl7n2t~AsHOB-Fh^_sA_<}?vGk+?1rilz*aE@a_ zZ67f;c7!lY<8*MWwwTRW^?l3WbgcmkXj{UXm&V3nC2Ltr-_`85g4h`BW-WW!-bXsf z`VPmMptzt>vYvam|DgRO(`(d<> z_xYCHEga8ATK& zM13-2q{J;i8vRC5PT$To8tTWg&T(;N$f7)f`bp2%^qxw22IXw>xu(*{d(Qm~aWu8HZTYk?p^Uqdc}^ z;*YU?oPG2Mxxqv?oPM5!GxR%4+d1-!q)V*pI`iJ9&t10fG1kMl22@jiOut%EovAz; z$p3m?g+}_mdv1XD)ENck!EEAT?hW3Gsg|CQ*Mc`&BUgnCoPGvj{auEckIksx|dZ~j76Sdd$i ztVlii?B4fg;Z%>5wtUlYZYcwfwUVJ+rSzaTyb`NS_`%q6US zIek{q=XI$Xwo|uzhY9u)TNvLO`*v)@fiBA8VEl3%-cf*J)^sf1TAX;HgNhy8L)qoB z+_e|o#C5hSNmV<>;sIkn+%a5Kv;Bzj6Y^)I`WH9wE?yZ;)P39`jn8arF=1Qmrkxv!cu{H3`VVdg7BUh0T2 zv#Q=I?tJf)@uSR&k;E9IcD}$E^0CB>WA5?vpSaT;lgtdomfg!z;5CyiX`c zFrQaG^Ekh<#&2dF1?q+gnL2aGcP)lO;!0?AZIo~Odw|fYn}SM<6d3*n$Na~?62@oeil!8tKixYQ-VtTk;ibot0yX{uc~{CN5niye2y21(s)VvZQXe6{F(&RcY;(spY8cwEYkVhxGNJ% z-MmB_?)z`MOAnge_zXgwKHsrupX9~&RJeyONg1Z}>#}PIth%j-4Y9qGq~X9giu-55 zD@lTZ^|tV%t(fzyq4k{+Q9llm#HkK0LjrkhtOBP0sgBWnIvJs;ZK_t&wz8w|-i zlXoYXlPtQI!;1d5?L&p#?l%&L26N%ay;bh~-gar|1h?)hG0)vY_%NTGBW)md6MeQe6k>xin|*5Y2na@=RTy1^7RY(L^YuE(ST6M57#-e1Yyc-OrU z@2P7deb}vvPu<_+$JYRSWBWTX;M}nE9%~`PHDAFwbOp9szp8;siX3!a8DlTM^V|3M z1D$$gz_^DkOt@xf+5_my{TY_T^(5YwzJ0i_;WxGwIk)WAqZsaNd%ZdWpI4k?qK_Zj zgHjw3!adcI#2<_GLKJuO z{;L2i=+Q^y6T3Ji2}@tK#VW>IP2F0yxApMEsQtsB->w^W?cpA_b{^Qr<2XqDVb*b! zF-o|OeX2*gI8FQ69x1p~wjI})qtkDRxJk?{V(*ad_6Wfhj(4)&)JmQ($1@(!b-8tz zoNg<2a^7jm{&?|~`tPh&CY9s&*ePh8Y7JG+ack`j6gsKW(Bs;X;a^=~oSGrJaDB3C zDinIQlNG(FvuE3de1JLMxnVyCrsiM-WuvG(#F&?J4CaN@R6mhMIi0u+;>VGXPffx^ zb5GP29L7xYSzL>nP5m6^oS%9E3(PIV!o9<=h_)rvFC$+;zS-Olc&C8hQ}Fzu1|7|Ygp(m zj4g7wzcCNpEI2RYVGpZDXY{t13VVxm;Xr)E+5izbiQ{RD01?5@svr50{ogRmA`T&q zfe3BJ6qX~7F%uXgnSN;&He!NBDH6W@kW8m;X5&baO`cmg0*lhzu$Z}*6>h|G@)e}B z>0D!HoHdPWu!;3-vsjPq#P8u8Q9JnwC`{vgK4Tm!9EOt?2`EX^5oauX#5wwwlFAwD zJimu7(0+yOtCX)>q~orICa&d1;~v-gvRL~A>YlQ`TD}wi%AyMI*mkYzfG_Nm*i|)X zWmyHuK2M=wxgHv$(xJbP{bSh&md|y>y*Iird%Xr-_YFsnQL!Q}%oH{p-`Vb~M<4n) zkUPD80T=qXzg7^z3zCHAzG3hp#&^_M_`Ozu|7$G~PF$2F_eYbg|4>HiK3-SEjHd5P z?g^bbst+7E&YZDt9%fSSNI83yf|yI(e11pf?Yn>lqhdrpZObiHvDwlOTSs{#^m{S( zP*=3i11DY^;4I^B;#$*n@|*kC;}*XgZ@->`2h8(`_NT1v8Rf5wPNISG3%=jp^ja4m z_YD-^sfSg75N{NOG(TUh(%twSXM#5AW5iI7i`B^Ya*gi!z&v!Iy<@r~I@4~JZiKGs zwYbOkldLHBwkk#6H?5J&-$!tI!@jXn6cIe1UgSP+I%5ERe2E*x&vwB6fnqrI$~_Gc zwm%q=)JN}E60wXQw?7l{{GLu^J;~JlF{z^%^`;jx={JTnmNbr(#e2d8@`;Q+iFqfd zuf-Irb|RObn|ZV)voG`adx#ZQPS{{&k4?Oex4c=2?UZ-%827W?A#xjv-x%NokMU&s zOPr-|S-J_z8TSI|BI(lp;o@?-Iy$|rm5k@{T;X}V$7}cj{i@%@iCSVB_n*Lr^dx+w z{!==Cp!2{tND(ic!T0sZTan~4VuU=ogrxLV7i}`ag>nX;;d_Qa{XhZQ601ehe(MJP zx5LnZI73pW3^Q~gnZ1n@bINnj)tpQWmM+K5MNKYy!(m*n-Vsgd$_UVk6S zzcALf4BqEh7vAxEv)5mc=Y3WAAosc)w8Eb3Wzc?C3Vo97WO*1IR6?hN1u*KBB8(57 zz&^g0Yf8E6yIM&%Qg@P`fiVv7eD>!4NxOrgLSb@(aAZ6u;#>}<2si57NAtPrpodU8 z(GLShU&P>E$-Zn0br&Y#Z}-2V#hg-4x`+8O40NW*$ z&%7Hh7N_x?@w{Goo*=IFibc=i{QhLD@#;HJP0Z_qoEtotfcNAd-i_lsVx94c{!`hn zQir-j_E55rI}{*pZ_k2KW-^pBxsS(s8roW`Li5lmJ_Bc=GMn$+e&@KF@An$F?(McG#woKiJPx%I-kH9s?pFsYBX0P24%_ zZa7cf#ms74h`{*3;&(1XwYcew|${b4I~I6OcY9gY;nhx1`Nwh+B~4;9wE8zrL>%VA5|Zftv4 zSMYspVm&^T!gDNtr=oWu9`!7P?+0(pUl)PF^bhV`j*#BnL?|)geD8DQSVzRrr&c`| z$@Cd@cs(+DKgHPIx|mGADb!6PT`5pSJ+JTiW4)2rdl=svD28)Rqh!@&H*BVTE2)t6 z9O)g4qaW(<$6-C3W!!S+IX^ZF7s)U6R>$Suqr{coKH@54-=V#dwg;4}4_olPg8=d5 za2@^{TY)zpbkRiG!953`S@*XOWe^`fNL=}wBC6xYL+ywaG}+b~cLh2}^w7J}1MO{x zfy?@6#rFz^a;>Es-`(kcWIfEsC85{Hhp@H{L+>NOxa`Pljr0A zS-2djL&T0Uc=P?60Uvw8mpRm(QpF(KM+l%CW@`xB?|TqMpQLdeF#1S8kvYy0B4eXL`hX`EBp%s5-d4Z!cVAt?N)CJr*rA?7JM z5{Vdc$s)e1KZ@;i`W@rgG3%(Tn8dm!e+n0l@7iN-A6?AnUc!~0)3Jha8ad9| zN_zq0?ESO=`^MK||EEzn%-W8QPvY;&oI_ck5b@G$2zLduJdU(G%@!N`r`9w$l9qwKFbe^+_$Ct*-)rwc|*sp6uO@;qdoV& z7-Z?9lbxzCqHIigUquK^vv#37*SUKXdx#LeA7V?reU_n6w6B4?T@pO)^f4eS1Oaw@ z|D5vB&m%D6a~VRiI5)#MD%?jANuQClMQ7C_)-FWE6QA(e6^U8=J%}tfjQX4=(mxO3 zJ-HReeO5r0U0+Pdx{8UkO)8#_DaDU4jXu+9o5MIcc7b9pbI)UYK4YG#Scs)rNm$01 zsr>F)%i}sH@xo^6wh&Wb7l++EhQiOL*w6Oq;&}eHlnr|D8m}Pc0%P6GQo=3T?q)Sg z?tk{cQ|g`-+l!ZM*V%PMJ&&P*xm6;Z@wRvY-ckQCD-&Pr67enTE~F(s?y*_L80)x)Z8Pl! zeOF<}v0>tO(q7iM|Ja{6%sj=!oaj4Jyq6@RoN+I59plP`(c=EHV%(U(_xO$t!<{d> zxXU`Kxc+s&Zz3Mh=LvH>o8ZUam+%mEeM?b)tUVgOa4%-x9K0oVNAviBeej9&gXd3< zYwq%#N8deqAyhcGrZ%w{8ec=PhvOP;&V%dp=g#n>wXrnp)B#Llh_{bfIhU@e>H{!eVo{K`MMHCOHN_{bwiowf3)`<08$j$->+d| z8Ceh%6-Cbx0}9AZ&umoKb;2fgk~6bAVgMFcUC|XFN|apOVZu zVB*YKQ4E{`@MPipz3!=+roWll0LFjM-LI=&)vH&pUcGu%-AlKlhM%AuyL_ZlGUh`X zzonNl37EXxN%iy2qUp%b9FFG*Uq1;Sz_T#uFn8VwG;jH{G=E-;vJmxW{Cz*=^yN=c zKHj}M8-1Mvx#xkevSc5st%LpWZ{vrkgOsa>_n>QrKS__jdWdq}@>aSYa&E+UZylab zx6PYO|H5;;JGPuecWv2Zn_l8o?gP*L!ylms!1HhP6~({zTE1l;TDAOOW%ZU8dU*Lm z^a$uj@%-m8@ZNdVQhbYJz3l~zY0dD-^zyt}^vag?^ctRly^XSWFrIg}1eK4_$Gnm! z>B}vj(^s(h$SHTzci=g>?-R6bc>|5zw8_?GMJr{uI;iJ}%c&RM=jz)!U3mlVTJ>*T zLIZHF-fw=K?DOxZ16xm^!7J9$kQEK|4(c8F9z@>!HMBeaJy{XjxmVmop4QRiUE#Dv z=i`}rYblLgF_y-)29>(jqm|8g9yn#i-pW+)%|P4Ykx$U<5ql|fTKm&U^ULV8`BRiL z(9c;T`YDT$U%U$UUq~+n@8u)zq{XdMlq*`-(o*zs4cc9Y^6Ohy(T#Y{P<>i2x_QJP zx(n^^1>b$(|9<}xS~1_FtX^>z{eAy^=&||3>Dq0FD4SZs$_vQ9IO00G7tcuFUhy)0 zfagaaqm2jOhx)Fyn!X?LF#UkB-?D8zZEpPn&+YG~TM_TJwXUZwTh~&z;=QT+%5{{z zwVHZtok_N>>uHzbW0Yb2e3Y~Ga@w`{&vXo)z4zPtKK%>-er+$*4_J8x^xLfDY z!3!$s5bz(mb+|HkbRb4Oij*<6+ywKEgiTJ^0&>TLswl$Ly&I? zo2D!V@S`{54$$z9oahv;^(01pif_uZ1mc!v4Dy ze_Of@eV>gz>}veY<=Qv0>FGy{l)F)O-?j_sfs)bi?}+jc+B{rhqeoV~PftS54_C&N zr%MLHru~(*+g_wMN~Tf~{$0bnkpJGcKhcNK^CRf+37)6Bdpu2ce4}sOs$J;wl161c z%D)2NhE?@S6!%VB!K)NkQWqc#=mvBLvVnhYZ0>Ro@H+6)rsgg$0w*Ki18E;%EpQm{ z4)8AUAutE{444O;16&Tg02~0^2doAj1eOCUfEkcC6KDdOffisEFdLWyoCwSXP6Aee ze;(2Wz(U{@;8fr=;B?>&;7s5w;A~(Ka1L-Ta6WJWa3OFp@E72(z@@-tz~#VV;0j;~ za3ydRuoSo&xCZze@OR)^;5y)X;0E9yz>UC7z|Fudz^%Y-z`uYyfIER@z+J$7!2Q4j zz`ucqfaSmnU?s2$SPeW3JOVrlJO(@tJOMljJOw-rJOexnJO?}vya2oiyacQPUItzP zUIktQ)&g$;Zvt-tZv*cD?*i`u?*ktI9|9i%9|NBNp8@ND&w($1^}v_FSHL&G2H?NI z_rMRpMqm@L8Tb+SAFu^z1-1g)0K$AKKo_7ZkOg!Dx&zsO4cGz?b*cTWG90(i)91I)+3<3@X1_MKYp}^t5FyPOC15g1c-~w`iJRlz^ z01AO4fFpqsKrv7P90j-m58wkzfij>1s02m=RX_j;0wEv_91TQ(YM=&)1IGZPfMbE< zfa8G^fYHDhU@TAzj04646M#BkA}|S<4AcWtfT_SVU^>tMGy*e#nLrcJ0?Yzt11AD= zfs=rff%(7!U?Fe{a4K*ba5``Xa3*jTa5k_AI1e}_b_CED(+ z@uZU@#+0c(U7gS|UCQl@ZH@VKZQ3d~%jja*FEX_?x68!anYv8!Gu3sbulBZStKW3> z>B`&NZ+rRD^^>lg?cqHSkVA+tTQn5R8_LZ(rlOye^0rOT(1nU1%u z^3KXi*DhVT&?BAA+G;1-o748TYcHQ!{?4E?&39+)!EMrwCtZ0*#vx>gv?FcP@wO!^ zUA-t5Da$wJ)AeU8??>UiF!BhwzebwLHXYG16|cEW z&>~GWUQx$3&SYo7XSTOlrjbw7iLy-c1@Et&vaUj3q029=cdGtUl?naKDf61gz&xqS z%x%-rW|=!8tD|i@%Aalw=5n)KW9%^3ncE7wBl9BKq)SuvnW`*ZdoDMQArqgdWBJBD zjCsLhPCMF%x&4mNX8k&2|BmQy?yobt3mGC6GR$jAl#A5NFUmRX%(zASOmYQJs??}& zDjRlIZmPc8D$6u})E#9{3Q|ZfXGPN`IpQ?SP zGIQHZ{bZ8!bJhu+M7pDWnaA9gypHm-JhQH5I#Zt=;T3X>IvV4Nv0U)}ky0L?alD-w z8{6Axb7Q_U^4hX_d*ru=CzWp8cW2wCk|)a2$uy6Z`O@(US>`sP{O3qJ(wER%r0vxw zmH%~AhOw`<+O}11l=WN8r_w)FSx0qGC8Hzr-_ibz{Hglp`gG;Sw!cPR=q2nU=ybXW zK6AZj$EkpshiTD16OU*ocm-{2Z!Ald7y6{CGnS>|Yj2rRPJ8o4*~WZ(+qai5mF-fs z<(QR94=xks##G1^WvOJQDiiHe)tSpu@piO7qs;cSHTIc`KUJBr56*Xn$Jnn~-Y-d; zb!R$NSvsA}ys7xw!^7>&Hc7Rw3i-ym_Q+4iWA2mr%w?j_w$hHaWB&HGF^*U0A;!e1 z$cs9bE6PO5b;i8lO_v&Zje2sqpp862PO36dmns$f##H1}@uw=w#4mJB)vmK;>11Zo z!`zpVH=R6gBltKq=FRe$PUR=5_;$2R=pp2!(%;M@c+F`#KC?{bGx8eqW|B>6OCY>yyYr6XXE9I%|uruu} z?3k(_QTJO;&9*Spoz>lFk5n>Km5IKL@zls?%nRO*q})D}3}c-!-;q9c7H_8hGu4^< z?+CAGYn(e#)|qr?$q{`U{X&%ek}2D=Gxq#BbZkqHw(2{h)6XHZGkSEijZyFR`lPW9 z_h)X`7Tr-De=dI3(>xY4{d4ubGv)r0I=8p4bbRT`ccwj3$ujn9%o}+{xk!z5#=NK( zsj+Tn=FRKKDBD=~bL!ES?6&HSI*WW~+K9Hlhjd5Bx1;TU4>l4yc4Vv_;T7`RN>j-+ zm$gN=)yHqS-aNKcx{9-l;7N*X(7X` zPbOY-U8c5~;+;wC+0Sj?9S8~`w;Cr z(^o1vLLR4W$xKz>mdv*5js2&~?+h8~WE;zWseX)orpu?&WoNE!?$_Lhm|u~m(=AiE zS!O2Q&eXAP+(){8j5gYlakN)AV}I>!^Gow}#11^3bhiDu^)~lwrZe^55#CI4L|sR0 zCfcM*)5$TH3;ji!N>*peggj1nrmvdy7rKf51l``0+ofvDW$F5AuWWPsRQ{TJWbTPo=xyOI0Ui zbtGkZLZ(Q$y~vBySe8mY>mztYnu<@zG3syT7j%2pm*7p;S6k)j+8gyV%MfkMshQ8B zli%$$+Ctb?*iY1na;A-S#&W^i*>q>h%A`ZOe$tf-S;ldTGEUoSXXLf=sr}Bfi)9zf z2P_|89|%?WB3*jzt0?_JkfLxIkzcze)2{va!_$2a*k9X^i0rgi587)XxVrV=nl4?s zXbt=H;Rs!e|Fk3jw(1G(v9H#wYd6}K=9lAOR)xA#5B$&TcSob3cLyMlJJ5c<%0Ou_ zS{VwCiF)1sQpy?2bqaOUS_R#KQfA{;M**T$Y;t|G9sjSjH`KLweP#9u_0#Jbn(G@I z?Biy&*c%#Kl2x;7o9b)FO{*JbPy7n7^sq>*N!OQqo2!2y;59_i^u=%XXX&c`K@N#{TS46w%?#M<#!hdM@WBd|srT zoy!f~bbE;1CVMAG2YuG^y6e4cKbMz$qnCCUn@+~LeEvRneOaU;OxbAFMcd-^=2GLg z`Su6DhS&5I8=U@2lqa@f+9OfiR)S{y5JCItD%!C>zLt=t(99oJO!UMmeAr6C$5DF1 zp{o;p?;(5H`ufWUXk6Wyi`O2w*6LD|T&KQ$=zq(ikVV28qHB@rE%@FxOec~Zq^=YG z(@EOzKs_Om-o5;j?>sSo8Fj1KTHJ-s2e*T24!dvw?E#T09XRFI186!L?MFZKdHqmY z4hB1om~hN+;)r+9P7HnxN@0{r;TwbUP#*KtCRM?FM=Q z*+0=gPhE2pGS)9?0d4*hYY(OD`1>W?23dU$U{U?Y$JYb2M+F+KyM|agiOEew3rbjD ziq>ar_R}2>|It_dr#rrF*6h1P$?q^teO?liDAM-s9f=+IYuylk zfb+LK4@0@Fm$Oi}lg)Hq;swD!k#^1DwbKV~t*;6@b<;}tK8{OJc}kXcXD2A#Vpn~t ziJ{qgBF{}-&q+OLK zf8+|HmPDix2aycMIg0r+8HdjiS|><-jdL0jgOnqLU7Gc&x!!azAsJb?5B)dxrh|s+ zw&I4neYuW-CkeOt@ZpD`q(GwNqd&jSY}ml z=<0*}gye$|e`PdAv^VN_tJn)*n~TRdVnY-u+h}ZJQg~*KbPi}df04gkZ}~#iZLy0f zn2529w6npa>z8_G9S9~Taf?iz5Yg&&xjQSExV@z^nw;DBr}Hy=h*&YgI)YBWg1YtL ziQvd$TrcTY&?zC^*LyVT<7|5c>0UP`;dKpqv&71suu&(Q6QwvVxAT|(MgG{-wwR|Y zh`R~>XQn&0TIPJEOF1net?lvVjAhoHxmx(BWn3-VS4l7DuT>oAik|mB_!lBv_NXUFx>R+Y{yx(L-##!0x?`mBLo3cmnFYi4) z6;}~=1zmGc#nnce4;if6d|Kk+YR>U(d1@|1i5WDWPx+KNt#N#93;z+{@MNfQR?Ea` zjdS>}zHWSNb4$frUv3lH>`0nw4*Mkp{=RmP_y@Y$!m803&m3l)qs6LTBhJ%1e_gQM zo?SL^AorTHnIAd)R`&&Q_bO6hJK;&=KNLAVo%7N5^ywdOPha%O_VmRc245?y@OkLs zR}&T3Z+qN5d!>@afuDbWn$=u8zHZEn`Uyf` zB}@F>K&zcrJC$pN_DWV_s)sCM!W^&`Kre-l@ zB}*(1O=t`L#ZB|HdW@uT;zS%7&6d!0|G#+Me*R&BvHgZD(H_BT*4SlYU2V&(CZ4aV z#FACA#M;eH%w%)on5JZj^IGqwy5_p3*^Tv0V_N3UsCznbvQe_c5oZ^E<~hb23RL>~ zKd;+e$r3lKeP%UJY8Fm3W?EgtQ7;Gi=CyBfTlTd#gYaEL#}xAfeViseX1{gJ#T_h-vnT_4o&av-hn%QPZ`qEnP<#bnVO^EBKVI{k7ttK2Q7$ zvaH|l*aEfqH!trF>`qzh|4fr6?Lxz9_yf}bv+N1wigUX5N!-PT@Dvp&3uK81{Z zFT0Z}`fRjaxAa;1?(IR@TQB^Ao;qWLZSQNdl&1&fDAVuCQSRKgk8*^+kMh)ceUzM` zBbB$`8kD`|%|B=)GsiI{;vR_qI^PL5Gt^_m40*pYm%`eE~20~Q6Lv<$G zI@H`;9>1t{Ih}=2MUaIK$dW^Fs2FgrOERmjg4}$kBT?^wQn?1nd8#u%IebTckyC}S zq>{OLJPk%NsWbF-<{3me9fiGB-LDRa2gT%K>i>~!Y3it+`i zs?Gw#>|+KUhDibGG!C%PrA@F9Rp=}%FenL28E4;_>nt?*vkMcPTPT&xFU&8*fQ2ZR z>NF||!x)^Ol~9XZ(#Tx^if_3QK1lOt4c?Kr7{AcRUj*v^ahbD$Qx63~&;C4l# zHEvfl0J6sCal0x)5x2_|ce`C)k1y!-S4G{fus0NR25LZ-S65f7Wsw@M(;vpg6YzPR zUc6!E41}Y&jEYvPA#Z85IxtE^Ui-bl4t9fQ0I|FP&J?1Ol3oG;#L)f)lniGU1v zR8@m+FUVj7e5J>L&y6x~Xp~oV2ayXzA<7>t_rfk{7RIEjo-mkW zp>PnB2hv+n<5jDIWe`;XK5qc>f|cmV?(-?TNTii;LHbOElsON2&t(@aGsN*&Blq zeKDBbSFQ#_v2wL66bu%4E8M{Xd;>V9#$)9%wJcl_L%&rq)mIHN7>VLi#zLdz?p&`w z5-5m=!-0a4-l`g>$6FCreeqy0-y6gw8apOX5avroB^)^HuVUY=VV8&B$Nfz%eU(nHdlW7{cc38ZK`sgsYtUbr z>y6Y@=0;;RURTsx8BxnUkzjsBWfiO83#gu8c|eU+#RIB0%&Nsv8!Gn(o$f$crRv9u zce%sikjqzkOvvR8#gGd{Las8mKjiX~z(H5^AhFsxD4Z196q5f*t0G)FP za+QZsRu&0{FrrY%6)26z)c~Rybb%;eJi@(=LRmBtSL36CaZG4MT&*fCjl+_-RJpy# zRrqiTl|lnI)Ch)-2^3b9MgoNu6>;P|zCdA^m5hYI7sa}QUvVxzGVY9xDvvuym50J` zc^@ubT>KTdR8_`8n7nX^S1oeUqeEzmidZF>tAfXd)p91|zDODTs=Qo{jqi4+ScqtqXszud`2i&gJm&PdadG?IxOihRXFN74hK&HZs2j|lpeN>p zmC!a^gIoxCEFF-s<#Jp(8&uLV$-Zh!!ztsT-=5 z`616AFT=%+vhr{#$|4m|EdufW8XqoEu)xfm10Vc~N5p*upj9{uDgH2qQ0{@celK_A zhZMgb`<9=zuZWJsC_Ttw+2i8p&TCNX@k2_?0}=rQ7Z3XIFpH-e{RX1Ac&j+)gHUgb zb1|sqcUJ}TB9RC#Y^zdOA`hMt%=3o4!8~|-Fs~FlNuE0ZJ7Yn_oDm;(zaO95YUop51JFvB0kcksbxyLq{nOex5^)#d{TwzpWen^V@Fp z_KKJ3(Dxk5nTIWWWK$ctCQdyx;y9EpLdxSCrX!+_2 z`stdk%C+meD&JhE(toc~sTJuF-Tpyu{%273Lz70*w(%qB#Ve1b&ycS<%R!ILbWm_& zKMGd&BUiV6G!^fGJ_P*tiQVWNd|+YmW%tn?|GtmzzxY1tbM!UjU3@jYaKJ8f<2NnY3fud!V%IDW) zDSnK7Q%hLk_ew{9em$LlcSZSq(P*d}y+0{SIlXc?H5YtBFCF$mc4hto>altOt@&{- zeYbwD_I~OoU(6+k?*aN~%Sm)^-%sccq^AWJQGdvtg7jnb-GKD4=E2%~tVO4HqvC#_ z(jwF^E?-D{KEK&^U{8m#8*Q>ZaM@sGx4A=bjoFQ1rcQ!3p&B`6{jNdYtmfqKP#6S|@$>?PsVI zrYl{`rYno)Pf=E0GDW!uI1Typ0e%;Ez@$3mn~8PGnDb_6@7sO~Th?uypnQPzp!>%u ztKi4{{_d3UEajEkXDEl_-Q&Z6j~32U2Gq<{p8B|68N2^%Wij68<#&3YUpGa$w{?oL z7~uDOuPmLaz1KVVyUEH61v8b=*H6~o@x2r8`HJ^`-ToQMBtQlDec=c5#wbVJGg|pO zprWq}&ls(}OZ?Vrqm_q;>F)=Z;2q&EOMkLmb^EurBlkRl&c5m=Ti-|pJ%sf1X74Zs`kjHABK{$$H5*kJqqqz$&?559mtI`#~DbW4mzK9Wy|pH)LQj~GPfW8U{b z{^-MfVNz2$Q!#^}{|24$}SE&v~m+MV`8{#~rc+4t^F%l18&Ui(WC zg&i@4-`#%nkuWu!6Q*(Jg{jZEVd}p7Lb`apm;QEEUpn&h-RT{)+Zx}UMjo>}JqQdP zvpWrhy!TOl*5iB9Essa(fJdU#kR7AGN5`o9MKLOTJxcr@b%ULYc+k>T1~fw;&it^PJ4BUQ_U4|dj8Znz5H~XUSAuhOCV=XYkxX6 z`VTq}et6<>4tn6`5z3)khb!5w4l48M?}YQa;ggHj+sbD5R$Sn_ANpN>>N;BZZWrQr z-Z>_S7|d~-V|bs2Y%T6{{O5guV>_=A`#m|tal87oUfTQd7cJ~f7f;)jPR2X(X98?@ zepjAt&-P`z4uGvVcJe#(yuNsz<~@5D9f;Tw*>4zqc*8Jl58+r`Hz-e=PhN|>2Z{ZN z=Y?a|3-gE5tuJ_$Y-yeP_T^?RWr=5Nm-S4}bI5zRV!*P1D zeFa@m@F@Lx>|?Za&0}(JesJhp#CrzESe|o^vAdo71u6TlrxM_5_j>X@_8kp+a1;G^ zRIA(;wx#ge(l{dl%Dv!TW-<62T9G5vpa(tW)@V6A+Th*dmwz@@&(<_^1E4@CvBgkoW3?-y4*j2NVNt z;5>lO2)xJeJ`x^%P4;e*?XGZ?y3~`A)x$wgK2TdGF+X^h|*FR6Yand4Ru-k$uc2+rhw@fUB>%6rFw zqi7f2@A-WCIKcZkpFgvTE6E$e#~cb({zr2B(@F7mJ9*s>hcjQi>rbI7eCcRw`)(eL|sCZ!@$g<#AgzyD$4K47YTM< zl&lR^@%#FPqE_!$GGl-aREt_Bokg-Xb_{Cew;V`sD@E0~RsL*%7@@|j{Ws?{X?j~x zNqX(}q(hoct4$7&$^4`mJPY{BgiumvmSiA=FBTL^!|Jti_+d;LzM~-8dflo+EtGQ< zh+*{dlLN%0>XM@LDU(WSY7``AzXFnzhSmF(ZM8{p%8uL-_*HU#by6y+lTu0OjL#^@ zSzxbI3#1zH8b=X6bs|O-Vl|v{QnWcrT2dpE!w&>8-lThLWP$8RAA%%x<7`J3$q$*3 zX4vHLbvsK(f|B_KNf*`omE+ah$`LV%Z<&Y@>CD^PS(VPODXHe$6Wlr}MS;ZUD5Tl7 zTBqz+Ba^lI93?x_7OW(_>>Ekuawu7tR8lA9bc4jFTVzL-n%7C4bvsL~bW*m3tqO~h zwu;x_TP03et0|cbGP=&Pqd*8zB0k6a7Ml zVPU?M%*?Lj`t_q$mEUpU#g5PTNY6nC!bd}7+Zs2IS1PUbn{>jU*8$1Wk%FV}HJm&- zDb+EoU#E$b%v!BWI+AWWSsM$g_>hULtqgKMQY)+!%(tx7MM=|Xq$)>L=6CQ2g~!IB zWb!jM+R|_)7i`prArwhb`jV6+M-{_(qZocc((ClL(iwEEq$Ba9;A1U94Q;^|$RTK2 zB`wLMA+~F^1# zlN0JbmV=D!H;BN9SrVqxNvQ_g84F3&YLb)dNGD}mjZAKf`jJVRPCvOBNIpSHQg17D zW+ii7azvWW$y1QtuN=PSX1P*Htg`LT8%~T~cBHGZz0SF^R(F1>1_ug!=1B}+U!k(C zzHnr}+JY^RwH!C{^R^@NlBe8CtTM!JA(@%+Q7=KpIRquWU5hI6YgnYUEtE=z5E)#_ z;|G)YESC_ak;xrLC#7L^QVw4ulMzX;HHnfdMQ7e#R5E<&wc9f%N2J$E)9IwNMkoTG zOrDV08rYDHBg&WGGb3)BpWNA*8Q;Pa%o1xRN)jQfwvNKOwQV#`E4fZ-`uB_G0IYn|r`sqvBS=+6WhXj2S zksWDdzHF->Yh^7+{wSRo0W^UJncP))`wO;j>a=nGc1)fQ~>n5Riju6=z7 z(#l%+6|Gc#Q^iwmJ>%w`Z1l$t)yS+q-?9-m?TNQ1Im}P&kGh09>ff4>$gBEsc$i|BlreR zrgBpBWhMPe-xiaiw7VEN`+BWp)_1Gz$z<5nYo*TmrYS|~JFM(Tua%99;EwW=8@+zZBWrc9lcatMl8a6!rLFje$gni Date: Thu, 17 Dec 2020 21:13:22 -0700 Subject: [PATCH 20/54] updated python tests and instructions for multi-threaded test --- .../kernels/compute_kernel_lp.pyx | 1 - .../Reservoirs/Level_Pool/Original_Makefile | 28 ------- .../Reservoirs/Level_Pool/module_levelpool.F | 1 - .../fortran/Reservoirs/bind_hybrid.f90 | 4 +- .../kernels/fortran/Reservoirs/bind_lp.f90 | 6 +- .../kernels/fortran/Reservoirs/bind_rfc.f90 | 2 + .../kernels/fortran/Reservoirs/makefile | 7 +- .../kernels/fortran/Reservoirs/test_binding.c | 20 +++-- .../Reservoir_Binding/kernels/readme | 34 +++++++-- .../Reservoir_Binding/kernels/setup.py | 2 +- .../kernels/test_compute_kernel.py | 76 ++++++++++++++----- 11 files changed, 113 insertions(+), 68 deletions(-) delete mode 100644 src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile diff --git a/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx index 2aeb39856..93b392179 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx +++ b/src/fortran_routing/Reservoir_Binding/kernels/compute_kernel_lp.pyx @@ -16,7 +16,6 @@ cdef extern void run_lp(void* handle, float *inflow, float *lateral_inflow, cdef extern void free_lp(void* handle); - ############ Other Reservoir Interface ############ #ctypedef enum compute_type: REACH, RESERVOIR_LP diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile deleted file mode 100644 index 03e1ef895..000000000 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/Original_Makefile +++ /dev/null @@ -1,28 +0,0 @@ - -include ../macros - -MODFLAG := -I ../MPP -I ../mod - -%.o : %.F - $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) $< - -.PHONY: all mod test - -all: mod - -mod: - #Build each sub module then build the module that depends on all sub modules - $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_properties.F - $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_state.F - $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool.F - $(COMPILER90) $(F90FLAGS) $(LDFLAGS) $(MODFLAG) -I$(NETCDFINC) module_levelpool_tests.F - ar -r ../lib/libHYDRO.a module_levelpool_properties.o - ar -r ../lib/libHYDRO.a module_levelpool_state.o - ar -r ../lib/libHYDRO.a module_levelpool.o - ar -r ../lib/libHYDRO.a module_levelpool_tests.o - - cp *.mod ../mod - -clean: - rm -f *.o - rm -f *.mod diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F index 3c903a205..e0de8b384 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/Level_Pool/module_levelpool.F @@ -159,7 +159,6 @@ subroutine run_levelpool_reservoir(this, previous_timestep_inflow, inflow, & real, intent(out) :: assimilated_value ! value assimilated from observation or forecast character(len=256), intent(out) :: assimilated_source_file ! source file of assimilated value - ! Update input variables this%input%inflow = inflow this%input%lateral_inflow = lateral_inflow diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 index 5a7e25338..b3a06549b 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_hybrid.f90 @@ -1,3 +1,5 @@ +! This module defines Fortran to C bindings for Persistence Level Pool Hybrid type reservoirs + module reservoir_to_c_hybrid use module_persistence_levelpool_hybrid @@ -31,7 +33,7 @@ SUBROUTINE init_hybrid(handle, water_elevation, & USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_F_POINTER, C_CHAR, c_null_char TYPE(C_PTR), INTENT(IN), VALUE :: handle real, intent(inout) :: water_elevation ! meters AMSL - real, intent(in) :: lake_area ! area of lake (km^2) + real, intent(in) :: lake_area ! area of lake (km^2) real, intent(in) :: weir_elevation ! bottom of weir elevation (meters AMSL) real, intent(in) :: weir_coeffecient ! weir coefficient real, intent(in) :: weir_length ! weir length (meters) diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 index 8940e7924..109b3c3a9 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_lp.f90 @@ -1,4 +1,6 @@ -module reservoir_to_c +! This module defines Fortran to C bindings for Level Pool type reservoirs + +module reservoir_to_c_lp use module_levelpool implicit none @@ -86,4 +88,4 @@ SUBROUTINE run_lp(handle, inflow, lateral_inflow, water_elevation, outflow, rout END SUBROUTINE run_lp -end module reservoir_to_c +end module reservoir_to_c_lp diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 index 051d0abe7..b2c3293f5 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/bind_rfc.f90 @@ -1,3 +1,5 @@ +! This module defines Fortran to C bindings for RFC type reservoirs + module reservoir_to_c_rfc use module_rfc_forecasts diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile index 9390b1216..fa960e44e 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/makefile @@ -10,9 +10,9 @@ VPATH = ./Level_Pool ./Persistence_Level_Pool_Hybrid ./RFC_Forecasts .PHONY: all -all: test_main +all: binding_lp.a bind_hybrid.a bind_rfc.a -test_main: test_main.o binding.a bind_hybrid.a bind_rfc.a +test_main: test_main.o binding_lp.a bind_hybrid.a bind_rfc.a gfortran -g -o $@ $^ test_main.o: test_binding.c @@ -30,7 +30,7 @@ bind_hybrid.o: module_persistence_levelpool_hybrid.o bind_rfc.o: module_rfc_forecasts.o -binding.a: module_reservoir.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o bind_lp.o +binding_lp.a: module_reservoir.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o bind_lp.o $(AR) $(ARFLAGS) $@ $^ bind_hybrid.a: module_reservoir.o module_hydro_stop.o module_reservoir_utilities.o module_persistence_levelpool_hybrid_properties.o module_persistence_levelpool_hybrid_state.o module_levelpool_properties.o module_levelpool_state.o module_levelpool.o module_reservoir_read_timeslice_data.o module_persistence_levelpool_hybrid.o bind_hybrid.o @@ -51,3 +51,4 @@ clean: rm -f *.mod rm -f *.a rm -f reservoir_tests + rm -f test_main diff --git a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c index 9feeff212..8454d3498 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c +++ b/src/fortran_routing/Reservoir_Binding/kernels/fortran/Reservoirs/test_binding.c @@ -1,10 +1,13 @@ #include -//#include + extern void* get_lp_handle(); + extern void init_lp(void* handle, float *water_elevation, float *lake_area, float *weir_elevation, float *weir_coefficient, float *weir_length, float *dam_length, float *orifice_elevation, float *orifice_coefficient, float *orifice_area, float *max_depth, int *lake_number); -//extern double run_lp(void* handle, float *previous_timestep_inflow, float *inflow, float *lateral_inflow, float *water_elevation, float *outflow, *routing_period, int *dynamic_reservoir_type, float *assimilated_value, char *assimilated_source_file[256]); + extern void run_lp(void* handle, float *previous_timestep_inflow, float *inflow, float *lateral_inflow, float *water_elevation, float *outflow, float *routing_period, int *dynamic_reservoir_type, float *assimilated_value, char *assimilated_source_file); + extern void free_lp(void* handle); + int main(int argc, char** argv) { float water_elevation = 0.0; @@ -19,9 +22,11 @@ int main(int argc, char** argv) float max_depth = 9.960000038146972656e+00; int lake_number = 16944276; void* test_p = get_lp_handle(); + init_lp(test_p, &water_elevation, &lake_area, &weir_elevation, &weir_coefficient, &weir_length, &dam_length, &orifice_elevation, &orifice_coefficient, &orifice_area, &max_depth, &lake_number); - float previous_timestep_inflow = 1.0; - float inflow = 1.0; + + float previous_timestep_inflow = 0.0; + float inflow = 0.0; float lateral_inflow = 0.0; float outflow = 0.0; float routing_period = 300.0; @@ -29,7 +34,10 @@ int main(int argc, char** argv) float assimilated_value = 0.0; char assimilated_source_file[256]; water_elevation = 9.73733330; - //double result = run_lp(test_p, &previous_timestep_inflow, &inflow, &lateral_inflow, &water_elevation, &outflow, &routing_period, &dynamic_reservoir_type, &assimilated_value, &assimilated_source_file); + run_lp(test_p, &previous_timestep_inflow, &inflow, &lateral_inflow, &water_elevation, &outflow, &routing_period, &dynamic_reservoir_type, &assimilated_value, assimilated_source_file); -//printf result; + + printf ("Outflow: %f\n", outflow); + printf("Complete \n"); } + diff --git a/src/fortran_routing/Reservoir_Binding/kernels/readme b/src/fortran_routing/Reservoir_Binding/kernels/readme index e3f7c6871..b1ec71726 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/readme +++ b/src/fortran_routing/Reservoir_Binding/kernels/readme @@ -1,10 +1,28 @@ -In order to run pytest for all reservoir types: +The Reservoir Module Requires NetCDF C and Fortran Libraries. +On a Linux system, the following environment variables should +be set if these libraries exist on that system: +1. Type "export NETCDF_LIB=/usr/lib64/openmpi/lib/" +2. Type "export NETCDF_INC=/usr/include/openmpi-x86_64" + + +In order to set up the tests for all reservoir types: +1. Activate a python3 virtual enviroment +2. Type "pip install Cython" +3. Type "pip install pytest" +4. Type "pip install pytest-parallel" +5. Navigate to "fortran/Reservoirs" directory +6. Type "make" which builds the Fortran Reservoir Module +7. Navigate back to "kernels" directory +8. Type "python setup.py install" which compiles and links the Cython handles to + each Reservoir type + + +Run the following tests: +1. Type "pytest" which runs 4 reservoirs in one process in one thread sequentially +2. Type "pytest --workers 4" which runs 4 reservoirs in parallel in 4 separate processes, + and each process runs a single thread +3. Type "pytest --tests-per-worker 4" which runs 4 reservoirs in 1 process on 4 parallel threads + +You should see all 4 tests passed for each of these 3 runs -1. Activate python virtual enviroment and pip install pytest -2. Navigate to "fortran/Reservoirs" directory -3. Type "make" -4. Navigate back to "kernels" directory -5. Type "python setup.py install" -6. Type "pytest test_compute_kernel.py" -Six tests should run and pass successfully diff --git a/src/fortran_routing/Reservoir_Binding/kernels/setup.py b/src/fortran_routing/Reservoir_Binding/kernels/setup.py index 5e5b0cd85..0e091b33b 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/setup.py +++ b/src/fortran_routing/Reservoir_Binding/kernels/setup.py @@ -14,7 +14,7 @@ include_dirs = [], libraries=['gfortran', 'netcdff', 'netcdf'], library_dirs=['/lib/gcc/x86_64-redhat-linux/4.8.5', '/usr/lib64/openmpi/lib/'], - extra_objects=['fortran/Reservoirs/binding.a']), + extra_objects=['fortran/Reservoirs/binding_lp.a']), Extension("compute_kernel_hybrid", sources = ["compute_kernel_hybrid.pyx"], diff --git a/src/fortran_routing/Reservoir_Binding/kernels/test_compute_kernel.py b/src/fortran_routing/Reservoir_Binding/kernels/test_compute_kernel.py index cd421902d..f9b973219 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/test_compute_kernel.py +++ b/src/fortran_routing/Reservoir_Binding/kernels/test_compute_kernel.py @@ -48,6 +48,34 @@ def lp_reservoir(): yield k +@pytest.fixture() +def lp_reservoir2(): + """ + an lp compute kernel to put under test + """ + print ("lp2 test") + + water_elevation = 9.70; + lake_area = 15.0949; + weir_elevation = 9.626; + weir_coefficient = 0.4; + weir_length = 10.0; + dam_length = 10.0; + orifice_elevation = 7.733; + orifice_coefficient = 0.1; + orifice_area = 1.0; + max_depth = 9.96; + lake_number = 16944277; + + k = lp_kernel(0, 1, + water_elevation, lake_area, + weir_elevation, weir_coefficient, weir_length, + dam_length, orifice_elevation, orifice_coefficient, + orifice_area, max_depth, lake_number) + yield k + + + @pytest.fixture() def hybrid_reservoir(): """ @@ -126,13 +154,6 @@ def rfc_reservoir(): yield k -def test_compute_kernel_lp(lp_reservoir): - """ - test a simple construction of LP cython object - """ - assert lp_reservoir is not None - - def test_lp_run(lp_reservoir): """ test running a LP reservoir @@ -161,14 +182,40 @@ def test_lp_run(lp_reservoir): expected_final_outflow = 17.0437641 + assert lp_reservoir is not None assert expected_final_outflow == pytest.approx(out) -def test_compute_kernel_hybrid(hybrid_reservoir): +def test_lp2_run(lp_reservoir2): """ - test a simple construction of hybrid cython object + test running a LP reservoir """ - assert hybrid_reservoir is not None + + inflow_list = [91.27196, 91.7394, 92.15904, 92.1518, 91.84663, \ + 91.38554, 90.86131, 90.32736, 89.81273, 89.3325, 88.89427, 88.5025, 88.16228, \ + 87.41539, 86.80043, 86.03979, 85.3849, 85.33451, 86.84274, 91.6084, 101.81398, \ + 118.85916, 143.99232, 177.7355, 219.2348, 267.22351, 319.90402, 374.54324, 428.86066, \ + 480.92096, 529.23584, 572.77673, 610.93237, 643.4389, 670.28516, 691.67767, 707.96088, \ + 719.57312, 726.96997, 730.63269, 731.03186, 728.61438, 723.79578, 716.9549, 708.43268, \ + 698.53247, 687.52112, 675.63123, 663.06421, 649.99976, 636.57898, 622.92926, 609.1745, \ + 595.40369, 581.68799, 568.08588, 554.64484, 541.4032, 528.39185, 515.63513, 503.14838, \ + 490.95123, 479.05109, 467.45493, 456.16663, 445.18753, 434.51706, 424.15311,414.0921, \ + 404.32956, 394.86014, 385.67789, 376.77621, 368.14966, 359.78958, 351.68875, 343.83972, \ + 336.23505, 328.86719, 321.7287, 314.81219, 308.11047, 301.61646, 295.32312, 289.22369, \ + 283.31207, 277.5813, 272.02521, 266.63776, 261.41315, 256.34564, 251.42978, 246.66023, \ + 242.03192, 237.53989, 233.17944, 228.94595, 224.83511, 220.84265, 216.96449, 213.19672, \ + 209.53554, 205.97734, 202.51857, 199.1559, 195.88605, 192.70595, 189.61255] + + routing_period = 300.0 + + for inflow in inflow_list: + out = lp_reservoir2.run(inflow, 0.0, routing_period) + print(out) + + expected_final_outflow = 15.5038433 + + assert lp_reservoir2 is not None + assert expected_final_outflow == pytest.approx(out) def test_compute_hybrid_run(hybrid_reservoir): @@ -200,16 +247,10 @@ def test_compute_hybrid_run(hybrid_reservoir): expected_final_outflow = 13.73367 + assert hybrid_reservoir is not None assert expected_final_outflow == pytest.approx(out) -def test_compute_kernel_rfc(rfc_reservoir): - """ - test a simple construction of RFC cython object - """ - assert rfc_reservoir is not None - - def test_compute_rfc_run(rfc_reservoir): """ test running a RFC reservoir @@ -235,5 +276,6 @@ def test_compute_rfc_run(rfc_reservoir): expected_final_outflow = 3.6 + assert rfc_reservoir is not None assert expected_final_outflow == pytest.approx(out) From 7bc918b82c750440a8cb1ff6db4a3e5d490f56de Mon Sep 17 00:00:00 2001 From: David Mattern Date: Thu, 17 Dec 2020 21:37:50 -0700 Subject: [PATCH 21/54] added todo in setup.py --- src/fortran_routing/Reservoir_Binding/kernels/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fortran_routing/Reservoir_Binding/kernels/setup.py b/src/fortran_routing/Reservoir_Binding/kernels/setup.py index 0e091b33b..bb4044d8f 100644 --- a/src/fortran_routing/Reservoir_Binding/kernels/setup.py +++ b/src/fortran_routing/Reservoir_Binding/kernels/setup.py @@ -2,6 +2,7 @@ from distutils.extension import Extension from Cython.Distutils import build_ext +#TODO: Imitate more general usage demonstrated in src/python_routing_v02/setup.py (OR integrate directly into that file...) #Document to set this up #When installing this module from source From 72b0610271c900911fd0a2fa18e8ad76696dd050 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 13:09:59 -0600 Subject: [PATCH 22/54] correct bad use of dict keyword --- .../troute/nhd_network_utilities_v02.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 0292973f5..a6d14010d 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -226,10 +226,10 @@ def set_supernetwork_data( return rv elif supernetwork == "Mainstems_CONUS": - dict = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) - dict.update( + rv.update( { "title_string": "CONUS 'Mainstems' (Channels below gages and AHPS prediction points)", # overwrites other title... "mask_file_path": os.path.join( @@ -241,7 +241,7 @@ def set_supernetwork_data( "mask_name": 1, # TODO: Not used yet. } ) - return dict + return rv # return { # "geo_file_path": os.path.join( From 1fb03fae38bf5dcca9ae368ef82fd5077ece79b3 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 13:24:14 -0600 Subject: [PATCH 23/54] change function to get_supernetwork_parameters --- .../troute/nhd_network_utilities_v02.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index a6d14010d..09f7ef6b8 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -2,7 +2,7 @@ import os -def set_supernetwork_data( +def set_supernetwork_parameters( supernetwork="", geo_input_folder=None, verbose=True, debuglevel=0 ): # TODO: consider managing path concatenation outside this function (and lose the os dependency) @@ -78,7 +78,7 @@ def set_supernetwork_data( }, } elif supernetwork == "Pocono_TEST2": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -121,7 +121,7 @@ def set_supernetwork_data( # } elif supernetwork == "LowerColorado_Conchos_FULL_RES": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -166,7 +166,7 @@ def set_supernetwork_data( } elif supernetwork == "Brazos_LowerColorado_FULL_RES": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -187,7 +187,7 @@ def set_supernetwork_data( return rv elif supernetwork == "Brazos_LowerColorado_Named_Streams": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -208,7 +208,7 @@ def set_supernetwork_data( return rv elif supernetwork == "CONUS_ge5": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -268,7 +268,7 @@ def set_supernetwork_data( # } elif supernetwork == "CONUS_Named_Streams": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -289,7 +289,7 @@ def set_supernetwork_data( return rv elif supernetwork == "CapeFear_FULL_RES": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( @@ -310,7 +310,7 @@ def set_supernetwork_data( return rv elif supernetwork == "Florence_FULL_RES": - rv = set_supernetwork_data( + rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder ) rv.update( From 2c98c9a51a32b633c28a64689b8f6627329e35f1 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 13:26:26 -0600 Subject: [PATCH 24/54] change call to set_supernetwork_parameters --- src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 49d3aa129..a8c776be3 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -475,7 +475,7 @@ def main(): start_time = time.time() # STEP 1 - network_data = nnu.set_supernetwork_data( + network_data = nnu.set_supernetwork_parameters( supernetwork=supernetwork, geo_input_folder=geo_input_folder, verbose=False, From 71829130a059b38a11666e2f31a47aaa09b2811a Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 13:48:01 -0600 Subject: [PATCH 25/54] change return to supernetwork_parameters --- .../compute_nhd_routing_SingleSeg_v02.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index a8c776be3..083c9a105 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -475,31 +475,31 @@ def main(): start_time = time.time() # STEP 1 - network_data = nnu.set_supernetwork_parameters( + supernetwork_parameters = nnu.set_supernetwork_parameters( supernetwork=supernetwork, geo_input_folder=geo_input_folder, verbose=False, debuglevel=debuglevel, ) - cols = network_data["columns"] - param_df = nhd_io.read(network_data["geo_file_path"]) + cols = supernetwork_parameters["columns"] + param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) param_df = param_df[list(cols.values())] param_df = param_df.set_index(cols["key"]) - if "mask_file_path" in network_data: + if "mask_file_path" in supernetwork_parameters: data_mask = nhd_io.read_mask( - network_data["mask_file_path"], - layer_string=network_data["mask_layer_string"], + supernetwork_parameters["mask_file_path"], + layer_string=supernetwork_parameters["mask_layer_string"], ) - param_df = param_df.filter(data_mask.iloc[:, network_data["mask_key"]], axis=0) + param_df = param_df.filter(data_mask.iloc[:, supernetwork_parameters["mask_key"]], axis=0) param_df = param_df.sort_index() param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) connections = nhd_network.extract_connections(param_df, cols["downstream"]) wbodies = nhd_network.extract_waterbodies( - param_df, cols["waterbody"], network_data["waterbody_null_code"] + param_df, cols["waterbody"], supernetwork_parameters["waterbody_null_code"] ) # # initial conditions, assume to be zero From 88f9bd042c53e0512912a41530f3992b464e2dbf Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 14:33:58 -0600 Subject: [PATCH 26/54] turn on custom supernetwork parameters --- .../compute_nhd_routing_SingleSeg_v02.py | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 083c9a105..273c3bdb0 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -289,6 +289,8 @@ def main(): args = _handle_args() custom_input_file = args.custom_input_file + supernetwork_parameters = None + waterbody_parameters = None if custom_input_file: ( @@ -409,21 +411,22 @@ def main(): supernetwork_parameters = { "title_string": "Custom Input Example (using Pocono Test Example datafile)", "geo_file_path": routelink_file, - "cols_as_text": False, - "key_col": 16, # "link", - "downstream_col": 22, # "to", - "length_col": 3, # "Length", - "manningn_col": 18, # "n", - "manningncc_col": 19, # "nCC", - "slope_col": 8, # "So", - "bottomwidth_col": 0, # "BtmWdth", - "topwidth_col": 9, # "TopWdth", - "topwidthcc_col": 10, # "TopWdthCC", - "waterbody_col": 6, # "NHDWaterbodyComID", + "columns": { + "key": "link", + "downstream": "to", + "dx": "Length", + "n": "n", # TODO: rename to `manningn` + "ncc": "nCC", # TODO: rename to `mannningncc` + "s0": "So", # TODO: rename to `bedslope` + "bw": "BtmWdth", # TODO: rename to `bottomwidth` + "waterbody": "NHDWaterbodyComID", + "tw": "TopWdth", # TODO: rename to `topwidth` + "twcc": "TopWdthCC", # TODO: rename to `topwidthcc` + "musk": "MusK", + "musx": "MusX", + "cs": "ChSlp", # TODO: rename to `sideslope` + }, "waterbody_null_code": -9999, - "MusK_col": 4, # "MusK", - "MusX_col": 5, # "MusX", - "ChSlp_col": 1, # "ChSlp", "terminal_code": 0, "driver_string": "NetCDF", "layer_string": 0, @@ -475,15 +478,17 @@ def main(): start_time = time.time() # STEP 1 - supernetwork_parameters = nnu.set_supernetwork_parameters( - supernetwork=supernetwork, - geo_input_folder=geo_input_folder, - verbose=False, - debuglevel=debuglevel, - ) + if not supernetwork_parameters: + supernetwork_parameters = nnu.set_supernetwork_parameters( + supernetwork=supernetwork, + geo_input_folder=geo_input_folder, + verbose=False, + debuglevel=debuglevel, + ) cols = supernetwork_parameters["columns"] param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) + param_df = param_df[list(cols.values())] param_df = param_df.set_index(cols["key"]) From f63a4c14934d692bef69577179f7535252623964 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 15:19:36 -0600 Subject: [PATCH 27/54] update comment headers --- .../compute_nhd_routing_SingleSeg_v02.py | 112 ++++++++++++------ 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 273c3bdb0..24abdfade 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -156,7 +156,7 @@ def _handle_args(): "CONUS_Named_Streams", "CONUS_FULL_RES_v20", "CapeFear_FULL_RES", - "Florence_FULL_RES" + "Florence_FULL_RES", ], # TODO: accept multiple or a Path (argparse Action perhaps) # action='append', @@ -472,12 +472,7 @@ def main(): qlat_file_index_col = "feature_id" qlat_file_value_col = "q_lateral" - if verbose: - print("creating supernetwork connections set") - if showtiming: - start_time = time.time() - - # STEP 1 + # STEP 0.5: Obtain Supernetwork Parameters for test cases if not supernetwork_parameters: supernetwork_parameters = nnu.set_supernetwork_parameters( supernetwork=supernetwork, @@ -486,6 +481,12 @@ def main(): debuglevel=debuglevel, ) + if verbose: + print("creating supernetwork connections set") + if showtiming: + start_time = time.time() + + # STEP 1: Build basic network connections graph cols = supernetwork_parameters["columns"] param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) @@ -497,7 +498,9 @@ def main(): supernetwork_parameters["mask_file_path"], layer_string=supernetwork_parameters["mask_layer_string"], ) - param_df = param_df.filter(data_mask.iloc[:, supernetwork_parameters["mask_key"]], axis=0) + param_df = param_df.filter( + data_mask.iloc[:, supernetwork_parameters["mask_key"]], axis=0 + ) param_df = param_df.sort_index() param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) @@ -507,18 +510,12 @@ def main(): param_df, cols["waterbody"], supernetwork_parameters["waterbody_null_code"] ) - # # initial conditions, assume to be zero - # # TODO: Allow optional reading of initial conditions from WRF - # q0 = pd.DataFrame( - # 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32" - # ) - if verbose: print("supernetwork connections set complete") if showtiming: print("... in %s seconds." % (time.time() - start_time)) - # STEP 2 + # STEP 2: Identify Independent Networks and Reaches by Network if showtiming: start_time = time.time() if verbose: @@ -563,8 +560,10 @@ def main(): ) else: # Set cold initial state + # assume to be zero + # 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" q0 = pd.DataFrame( - 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" + 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32", ) if verbose: @@ -609,13 +608,69 @@ def main(): print("qlateral array complete") if showtiming: print("... in %s seconds." % (time.time() - start_time)) - start_time = time.time() + + ################### Main Execution Loop across ordered networks + if showtiming: + main_start_time = time.time() + if verbose: + print(f"executing routing computation ...") if compute_method == "standard cython compute network": compute_func = mc_reach.compute_network else: compute_func = mc_reach.compute_network + results = compute_nhd_routing_v02( + reaches_bytw, + compute_func, + parallel_compute_method, + cpu_pool, + nts, + qts_subdivisions, + independent_networks, + param_df, + qlats, + q0, + assume_short_ts, + ) + + if (debuglevel <= -1) or csv_output_folder: + qvd_columns = pd.MultiIndex.from_product( + [range(nts), ["q", "v", "d"]] + ).to_flat_index() + flowveldepth = pd.concat( + [pd.DataFrame(d, index=i, columns=qvd_columns) for i, d in results], + copy=False, + ) + + if csv_output_folder: + flowveldepth = flowveldepth.sort_index() + output_path = pathlib.Path(csv_output_folder).resolve() + flowveldepth.to_csv(output_path.joinpath(f"{args.supernetwork}.csv")) + + if debuglevel <= -1: + print(flowveldepth) + + if verbose: + print("ordered reach computation complete") + if showtiming: + print("... in %s seconds." % (time.time() - start_time)) + + +def compute_nhd_routing_v02( + reaches_bytw, + compute_func, + parallel_compute_method, + cpu_pool, + nts, + qts_subdivisions, + independent_networks, + param_df, + qlats, + q0, + assume_short_ts, +): + if parallel_compute_method == "by-network": with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: jobs = [] @@ -666,28 +721,7 @@ def main(): ) ) - if (debuglevel <= -1) or csv_output_folder: - qvd_columns = pd.MultiIndex.from_product( - [range(nts), ["q", "v", "d"]] - ).to_flat_index() - flowveldepth = pd.concat( - [pd.DataFrame(d, index=i, columns=qvd_columns) for i, d in results], - copy=False, - ) - - if csv_output_folder: - flowveldepth = flowveldepth.sort_index() - output_path = pathlib.Path(csv_output_folder).resolve() - flowveldepth.to_csv(output_path.joinpath(f"{args.supernetwork}.csv")) - - if debuglevel <= -1: - print(flowveldepth) - - if verbose: - print("ordered reach computation complete") - if showtiming: - print("... in %s seconds." % (time.time() - start_time)) - + return results if __name__ == "__main__": main() From 80666bf1dd9042f0aa358bb49a71427f1c6cb080 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 15:20:41 -0600 Subject: [PATCH 28/54] reorganize param_df edits into one place --- .../compute_nhd_routing_SingleSeg_v02.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 24abdfade..b882d1b5d 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -506,10 +506,19 @@ def main(): param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) connections = nhd_network.extract_connections(param_df, cols["downstream"]) + # TODO: reorganize this so the wbodies object doesn't use the par-final param_df + # This could mean doing something different to get the final param_df, + # or changing the wbodies call to use the final param_df as it stands. wbodies = nhd_network.extract_waterbodies( param_df, cols["waterbody"], supernetwork_parameters["waterbody_null_code"] ) + param_df["dt"] = dt + param_df = param_df.rename(columns=nnu.reverse_dict(cols)) + param_df = param_df.astype("float32") + + # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] + if verbose: print("supernetwork connections set complete") if showtiming: @@ -533,15 +542,6 @@ def main(): if showtiming: print("... in %s seconds." % (time.time() - start_time)) - if showtiming: - start_time = time.time() - - param_df["dt"] = dt - param_df = param_df.rename(columns=nnu.reverse_dict(cols)) - param_df = param_df.astype("float32") - - # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] - # STEP 4: Handle Channel Initial States if showtiming: start_time = time.time() @@ -723,5 +723,6 @@ def compute_nhd_routing_v02( return results + if __name__ == "__main__": main() From fa6e0cda4bae6a990a193a2ddc750f306706fdc1 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 16:23:59 -0600 Subject: [PATCH 29/54] organize functions above main. --- .../compute_nhd_routing_SingleSeg_v02.py | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index b882d1b5d..d73866199 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -285,6 +285,73 @@ def constant_qlats(index_dataset, nsteps, qlat): return ql +def compute_nhd_routing_v02( + reaches_bytw, + compute_func, + parallel_compute_method, + cpu_pool, + nts, + qts_subdivisions, + independent_networks, + param_df, + qlats, + q0, + assume_short_ts, +): + + if parallel_compute_method == "by-network": + with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: + jobs = [] + for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): + r = list(chain.from_iterable(reach_list)) + param_df_sub = param_df.loc[ + r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + ].sort_index() + qlat_sub = qlats.loc[r].sort_index() + q0_sub = q0.loc[r].sort_index() + jobs.append( + delayed(compute_func)( + nts, + qts_subdivisions, + reach_list, + independent_networks[tw], + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, + qlat_sub.values, + q0_sub.values, + assume_short_ts, + ) + ) + results = parallel(jobs) + + else: # Execute in serial + results = [] + for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): + r = list(chain.from_iterable(reach_list)) + param_df_sub = param_df.loc[ + r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + ].sort_index() + qlat_sub = qlats.loc[r].sort_index() + q0_sub = q0.loc[r].sort_index() + results.append( + compute_func( + nts, + qts_subdivisions, + reach_list, + independent_networks[tw], + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, + qlat_sub.values, + q0_sub.values, + assume_short_ts, + ) + ) + + return results + + def main(): args = _handle_args() @@ -657,72 +724,5 @@ def main(): print("... in %s seconds." % (time.time() - start_time)) -def compute_nhd_routing_v02( - reaches_bytw, - compute_func, - parallel_compute_method, - cpu_pool, - nts, - qts_subdivisions, - independent_networks, - param_df, - qlats, - q0, - assume_short_ts, -): - - if parallel_compute_method == "by-network": - with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: - jobs = [] - for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): - r = list(chain.from_iterable(reach_list)) - param_df_sub = param_df.loc[ - r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] - ].sort_index() - qlat_sub = qlats.loc[r].sort_index() - q0_sub = q0.loc[r].sort_index() - jobs.append( - delayed(compute_func)( - nts, - qts_subdivisions, - reach_list, - independent_networks[tw], - param_df_sub.index.values, - param_df_sub.columns.values, - param_df_sub.values, - qlat_sub.values, - q0_sub.values, - assume_short_ts, - ) - ) - results = parallel(jobs) - - else: # Execute in serial - results = [] - for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): - r = list(chain.from_iterable(reach_list)) - param_df_sub = param_df.loc[ - r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] - ].sort_index() - qlat_sub = qlats.loc[r].sort_index() - q0_sub = q0.loc[r].sort_index() - results.append( - compute_func( - nts, - qts_subdivisions, - reach_list, - independent_networks[tw], - param_df_sub.index.values, - param_df_sub.columns.values, - param_df_sub.values, - qlat_sub.values, - q0_sub.values, - assume_short_ts, - ) - ) - - return results - - if __name__ == "__main__": main() From da4e34c82c08a052b89f4628c075b7de430b5830 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 16:24:51 -0600 Subject: [PATCH 30/54] move build_connections into nhd_network_utilities_v02 --- .../troute/nhd_network_utilities_v02.py | 38 +++++++++++++++++++ .../compute_nhd_routing_SingleSeg_v02.py | 32 +--------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 09f7ef6b8..3a4ebbdfd 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -1,6 +1,8 @@ import json import os +import troute.nhd_io as nhd_io +import troute.nhd_network as nhd_network def set_supernetwork_parameters( supernetwork="", geo_input_folder=None, verbose=True, debuglevel=0 @@ -392,3 +394,39 @@ def reverse_dict(d): Values must be hashable! """ return {v: k for k, v in d.items()} + +def build_connections(supernetwork_parameters, dt): + + cols = supernetwork_parameters["columns"] + param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) + + param_df = param_df[list(cols.values())] + param_df = param_df.set_index(cols["key"]) + + if "mask_file_path" in supernetwork_parameters: + data_mask = nhd_io.read_mask( + supernetwork_parameters["mask_file_path"], + layer_string=supernetwork_parameters["mask_layer_string"], + ) + param_df = param_df.filter( + data_mask.iloc[:, supernetwork_parameters["mask_key"]], axis=0 + ) + + param_df = param_df.sort_index() + param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) + + connections = nhd_network.extract_connections(param_df, cols["downstream"]) + # TODO: reorganize this so the wbodies object doesn't use the par-final param_df + # This could mean doing something different to get the final param_df, + # or changing the wbodies call to use the final param_df as it stands. + wbodies = nhd_network.extract_waterbodies( + param_df, cols["waterbody"], supernetwork_parameters["waterbody_null_code"] + ) + + param_df["dt"] = dt + param_df = param_df.rename(columns=reverse_dict(cols)) + param_df = param_df.astype("float32") + + # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] + return connections, wbodies, param_df + diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index d73866199..0e2280a92 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -554,37 +554,7 @@ def main(): start_time = time.time() # STEP 1: Build basic network connections graph - cols = supernetwork_parameters["columns"] - param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) - - param_df = param_df[list(cols.values())] - param_df = param_df.set_index(cols["key"]) - - if "mask_file_path" in supernetwork_parameters: - data_mask = nhd_io.read_mask( - supernetwork_parameters["mask_file_path"], - layer_string=supernetwork_parameters["mask_layer_string"], - ) - param_df = param_df.filter( - data_mask.iloc[:, supernetwork_parameters["mask_key"]], axis=0 - ) - - param_df = param_df.sort_index() - param_df = nhd_io.replace_downstreams(param_df, cols["downstream"], 0) - - connections = nhd_network.extract_connections(param_df, cols["downstream"]) - # TODO: reorganize this so the wbodies object doesn't use the par-final param_df - # This could mean doing something different to get the final param_df, - # or changing the wbodies call to use the final param_df as it stands. - wbodies = nhd_network.extract_waterbodies( - param_df, cols["waterbody"], supernetwork_parameters["waterbody_null_code"] - ) - - param_df["dt"] = dt - param_df = param_df.rename(columns=nnu.reverse_dict(cols)) - param_df = param_df.astype("float32") - - # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] + connections, wbodies, param_df = nnu.build_connections(supernetwork_parameters, dt) if verbose: print("supernetwork connections set complete") From f988c4178dcfae1d9b21431b0a6718d455aab7ff Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 18:23:52 -0600 Subject: [PATCH 31/54] move reach organization to utility function --- .../troute/nhd_network_utilities_v02.py | 13 +++++++++++++ .../compute_nhd_routing_SingleSeg_v02.py | 9 +++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 3a4ebbdfd..77c553f2d 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -1,6 +1,7 @@ import json import os +from functools import partial import troute.nhd_io as nhd_io import troute.nhd_network as nhd_network @@ -430,3 +431,15 @@ def build_connections(supernetwork_parameters, dt): # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] return connections, wbodies, param_df +def organize_independent_networks(connections): + + rconn = nhd_network.reverse_network(connections) + independent_networks = nhd_network.reachable_network(rconn) + reaches_bytw = {} + for tw, net in independent_networks.items(): + path_func = partial(nhd_network.split_at_junction, net) + reaches_bytw[tw] = nhd_network.dfs_decomposition(net, path_func) + + return independent_networks, reaches_bytw, rconn + + diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 0e2280a92..f0388f6ca 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -567,12 +567,9 @@ def main(): if verbose: print("organizing connections into reaches ...") - rconn = nhd_network.reverse_network(connections) - independent_networks = nhd_network.reachable_network(rconn) - reaches_bytw = {} - for tw, net in independent_networks.items(): - path_func = partial(nhd_network.split_at_junction, net) - reaches_bytw[tw] = nhd_network.dfs_decomposition(net, path_func) + independent_networks, reaches_bytw, rconn = nnu.organize_independent_networks( + connections + ) if verbose: print("reach organization complete") From b3c4f3ad61f96608d8c92fa9d01ecfdd4193b908 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 18:25:54 -0600 Subject: [PATCH 32/54] add TODO to remove dependence on dt in build_connections This will require some deep surgery into mc_reach.pyx, but should be doable. --- src/python_framework_v02/troute/nhd_network_utilities_v02.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 77c553f2d..7d4375ab6 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -397,6 +397,7 @@ def reverse_dict(d): return {v: k for k, v in d.items()} def build_connections(supernetwork_parameters, dt): + # TODO: Remove the dependence on dt in this function cols = supernetwork_parameters["columns"] param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) From cdacb43bbdbdcb5352d0e527d4f250ed97f38bf9 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 21:58:01 -0600 Subject: [PATCH 33/54] make function for q0 --- .../troute/nhd_network_utilities_v02.py | 26 +++++++++++++++++++ .../compute_nhd_routing_SingleSeg_v02.py | 18 +------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 7d4375ab6..e7ea925d4 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -1,6 +1,7 @@ import json import os +import pandas as pd from functools import partial import troute.nhd_io as nhd_io import troute.nhd_network as nhd_network @@ -444,3 +445,28 @@ def organize_independent_networks(connections): return independent_networks, reaches_bytw, rconn +def build_channel_initial_state(restart_parameters, channel_index=None): + + wrf_hydro_channel_restart_file = restart_parameters.get("wrf_hydro_channel_restart_file",None) + + if wrf_hydro_channel_restart_file: + + q0 = nhd_io.get_stream_restart_from_wrf_hydro( + restart_parameters["wrf_hydro_channel_restart_file"], + restart_parameters["wrf_hydro_channel_ID_crosswalk_file"], + restart_parameters["wrf_hydro_channel_ID_crosswalk_file_field_name"], + restart_parameters["wrf_hydro_channel_restart_upstream_flow_field_name"], + restart_parameters["wrf_hydro_channel_restart_downstream_flow_field_name"], + restart_parameters["wrf_hydro_channel_restart_depth_flow_field_name"], + ) + else: + # Set cold initial state + # assume to be zero + # 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" + q0 = pd.DataFrame( + 0, index=channel_index, columns=["qu0", "qd0", "h0"], dtype="float32", + ) + + return q0 + + diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index f0388f6ca..1bc042b79 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -582,23 +582,7 @@ def main(): if verbose: print("setting channel initial states ...") - if wrf_hydro_channel_restart_file: - - q0 = nhd_io.get_stream_restart_from_wrf_hydro( - wrf_hydro_channel_restart_file, - wrf_hydro_channel_ID_crosswalk_file, - wrf_hydro_channel_ID_crosswalk_file_field_name, - wrf_hydro_channel_restart_upstream_flow_field_name, - wrf_hydro_channel_restart_downstream_flow_field_name, - wrf_hydro_channel_restart_depth_flow_field_name, - ) - else: - # Set cold initial state - # assume to be zero - # 0, index=connections.keys(), columns=["qu0", "qd0", "h0",], dtype="float32" - q0 = pd.DataFrame( - 0, index=param_df.index, columns=["qu0", "qd0", "h0"], dtype="float32", - ) + q0 = nnu.build_channel_initial_state(restart_parameters, param_df.index) if verbose: print("channel initial states complete") From 86cfc7be12df13696fdfd6949ce0efc55a804276 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 21:58:45 -0600 Subject: [PATCH 34/54] add function for qlats --- .../troute/nhd_network_utilities_v02.py | 33 +++++++++++++++++++ .../compute_nhd_routing_SingleSeg_v02.py | 26 +-------------- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index e7ea925d4..478f3b5db 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -470,3 +470,36 @@ def build_channel_initial_state(restart_parameters, channel_index=None): return q0 +def build_qlateral_array(forcing_parameters, connection_keys): + + qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) + qlat_input_file = forcing_parameters.get("qlat_input_file", None) + if qlat_input_folder: + qlat_file_pattern_filter = forcing_parameters.get("qlat_file_pattern_filter","*CHRT_OUT*") + qlat_file_index_col = forcing_parameters.get("qlat_file_index_col","feature_id") + qlat_file_value_col = forcing_parameters.get("qlat_file_value_col","q_lateral") + qlat_files = glob.glob(qlat_input_folder + qlat_file_pattern_filter) + qlat_df = nhd_io.get_ql_from_wrf_hydro( + qlat_files=qlat_files, + index_col=qlat_file_index_col, + value_col=qlat_file_value_col, + ) + + qlat_df = qlat_df[qlat_df.index.isin(connections_keys)] + + # TODO: These four lines seem extraneous + # df_length = len(qlat_df.columns) + # for x in range(df_length, 144): + # qlat_df[str(x)] = 0 + # qlat_df = qlat_df.astype("float32") + + elif qlat_input_file: + qlat_df = nhd_io.get_ql_from_csv(qlat_input_file) + + else: + qlat_df = pd.DataFrame( + qlat_const, index=connections.keys(), columns=range(nts), dtype="float32", + ) + + return qlat_df + diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 1bc042b79..f0fa15d52 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -596,31 +596,7 @@ def main(): if verbose: print("creating qlateral array ...") - if qlat_input_folder: - qlat_files = glob.glob(qlat_input_folder + qlat_file_pattern_filter) - qlat_df = nhd_io.get_ql_from_wrf_hydro( - qlat_files=qlat_files, - index_col=qlat_file_index_col, - value_col=qlat_file_value_col, - ) - - qlat_df = qlat_df[qlat_df.index.isin(connections.keys())] - df_length = len(qlat_df.columns) - - # TODO: These three lines seem extraneous - # for x in range(df_length, 144): - # qlat_df[str(x)] = 0 - # qlat_df = qlat_df.astype("float32") - - elif qlat_input_file: - qlat_df = nhd_io.get_ql_from_csv(qlat_input_file) - - else: - qlat_df = pd.DataFrame( - qlat_const, index=connections.keys(), columns=range(nts), dtype="float32", - ) - - qlats = qlat_df + qlats = nnu.build_qlateral_array(forcing_parameters, connections.keys()) if verbose: print("qlateral array complete") From bdddd93a3186889ed54ad6ea029f11ae59a660d6 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 22:01:27 -0600 Subject: [PATCH 35/54] separate _input_handler function Also a number of black/formatting changes --- src/python_framework_v02/troute/nhd_io.py | 6 +- .../troute/nhd_network.py | 4 +- .../troute/nhd_network_augment.py | 424 ++++++++++-------- .../troute/nhd_network_utilities_v02.py | 34 +- .../compute_nhd_routing_SingleSeg_v02.py | 379 +++++++++------- 5 files changed, 480 insertions(+), 367 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index 7d50a4003..338f50415 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -6,6 +6,7 @@ import json import yaml + def read_netcdf(geo_file_path): with xr.open_dataset(geo_file_path) as ds: return ds.to_dataframe() @@ -62,6 +63,7 @@ def read_custom_input(custom_input_file): run_parameters, ) + def replace_downstreams(data, downstream_col, terminal_code): ds0_mask = data[downstream_col] == terminal_code new_data = data.copy() @@ -219,8 +221,8 @@ def get_stream_restart_from_wrf_hydro( qdf2 = qdf2.reset_index().set_index([channel_ID_column]) q_initial_states = qdf2 - - q_initial_states = q_initial_states.drop(columns = "index") + + q_initial_states = q_initial_states.drop(columns="index") return q_initial_states diff --git a/src/python_framework_v02/troute/nhd_network.py b/src/python_framework_v02/troute/nhd_network.py index 6ef77bcfd..5702141d5 100644 --- a/src/python_framework_v02/troute/nhd_network.py +++ b/src/python_framework_v02/troute/nhd_network.py @@ -238,6 +238,7 @@ def dfs_decomposition_depth_tuple(N, path_func, source_nodes=None): return path_tuples + def tuple_with_orders_into_dict(tuple_list, key_idx=0, val_idx=1): """ Append data values from a set of tuples of form (key, data) @@ -256,7 +257,8 @@ def tuple_with_orders_into_dict(tuple_list, key_idx=0, val_idx=1): load_dict[t[key_idx]].append(t[val_idx]) return load_dict - + + def dfs_decomposition(N, path_func, source_nodes=None): """ Decompose N into a list of simple segments. diff --git a/src/python_framework_v02/troute/nhd_network_augment.py b/src/python_framework_v02/troute/nhd_network_augment.py index f01d3fae2..c935cac2c 100644 --- a/src/python_framework_v02/troute/nhd_network_augment.py +++ b/src/python_framework_v02/troute/nhd_network_augment.py @@ -16,22 +16,23 @@ sys.path.append(os.path.join(root, "src", "python_framework_v01")) import nhd_network_utilities_v02 as nnu -import nhd_network +import nhd_network import nhd_io import network_dl + def _handle_args(): - + parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter ) parser.add_argument( "-t", "--threshold_length", - help = "threshold segment length (meters)", - dest = "threshold", - default = 500, - type = int, + help="threshold segment length (meters)", + dest="threshold", + default=500, + type=int, ) parser.add_argument( "--network", @@ -49,58 +50,59 @@ def _handle_args(): "CONUS_Named_Streams", "CONUS_FULL_RES_v20", "CapeFear_FULL_RES", - "Florence_FULL_RES" + "Florence_FULL_RES", ], default="CapeFear_FULL_RES", ) parser.add_argument( "-p", "--prune", - help = "prune short headwater reaches, 1 if yes, 0 if no", - dest = "prune", + help="prune short headwater reaches, 1 if yes, 0 if no", + dest="prune", action="store_true", ) parser.add_argument( "-s", "--snap", - help = "snap junctions adjacent to short reaches, 1 if yes, 0 if no", - dest = "snap", - action = "store_true" + help="snap junctions adjacent to short reaches, 1 if yes, 0 if no", + dest="snap", + action="store_true", ) parser.add_argument( "-return_original", "--return_original", - help = "return an unmodified RouteLink.nc file for the specified domain", - dest = "return_original", - action = "store_true" + help="return an unmodified RouteLink.nc file for the specified domain", + dest="return_original", + action="store_true", ) - + return parser.parse_args() - + + def get_network_data(network_name): - + # Create directory path variable for test/input/geo, where NHD data and masks are stored test_folder = os.path.join(root, r"test") geo_input_folder = os.path.join(test_folder, r"input", r"geo") - + # Load network meta data for the Cape Fear Basin supernetwork = network_name network_data = nnu.set_supernetwork_data( supernetwork=supernetwork, geo_input_folder=geo_input_folder ) - + # if the NHDPlus RouteLink file does not exist, download it. if not os.path.exists(network_data["geo_file_path"]): filename = os.path.basename(network_data["geo_file_path"]) network_dl.download(network_data["geo_file_path"], network_data["data_link"]) - + # read-in NHD data, retain copies for viz- and full network analysis purposes - RouteLink = nhd_io.read(network_data["geo_file_path"]) - + RouteLink = nhd_io.read(network_data["geo_file_path"]) + # select only the necessary columns of geospatial data, set the DataFrame index cols = [v for c, v in network_data["columns"].items()] # GET THE STRAHLER ORDER DATA TOO! - cols.append('order') + cols.append("order") data = nhd_io.read(network_data["geo_file_path"]) data = data[cols] @@ -109,7 +111,8 @@ def get_network_data(network_name): # mask NHDNetwork to isolate test network - full resolution Cape Fear basin, NC if "mask_file_path" in network_data: data_mask = nhd_io.read_mask( - network_data["mask_file_path"], layer_string=network_data["mask_layer_string"], + network_data["mask_file_path"], + layer_string=network_data["mask_layer_string"], ) data = data.filter(data_mask.iloc[:, network_data["mask_key"]], axis=0) @@ -118,12 +121,12 @@ def get_network_data(network_name): # replace downstreams data = nhd_io.replace_downstreams(data, network_data["columns"]["downstream"], 0) - + return data, RouteLink, network_data def network_connections(data, network_data): - + """ Extract upstream and downstream connections between segments in network Args: @@ -133,17 +136,18 @@ def network_connections(data, network_data): conn (dict): downstream connections rconn (dict): upstream connections """ - + # extract downstream connections conn = nhd_network.extract_connections(data, network_data["columns"]["downstream"]) - + # extract upstream connections rconn = nhd_network.reverse_network(conn) - + return conn, rconn + def build_reaches(rconn): - + # isolate independent subnetworks subnets = nhd_network.reachable_network(rconn) @@ -155,11 +159,12 @@ def build_reaches(rconn): for tw, net in subnets.items(): path_func = partial(nhd_network.split_at_junction, net) subreaches[tw] = nhd_network.dfs_decomposition(net, path_func) - + return subreachable, subreaches, subnets + def prune_headwaters(data, threshold, network_data): - + # initialize list to store pruned reaches hw_prune_list = [] @@ -170,7 +175,9 @@ def prune_headwaters(data, threshold, network_data): # --------------------------------------# # build connections and reverse connections - connections, rconn = network_connections(data.drop(index = chain.from_iterable(hw_prune_list)), network_data) + connections, rconn = network_connections( + data.drop(index=chain.from_iterable(hw_prune_list)), network_data + ) # identify headwater segments hws = connections.keys() - chain.from_iterable(connections.values()) @@ -179,7 +186,7 @@ def prune_headwaters(data, threshold, network_data): subreachable, subreaches, subnets = build_reaches(rconn) # find headwater reaches - hw_reaches = [] + hw_reaches = [] for sublists in list(subreaches.values()): for rch in sublists: for val in rch: @@ -192,14 +199,14 @@ def prune_headwaters(data, threshold, network_data): # find headwater reaches shorter than threshold short_hw_reaches = [] for rch in hw_reaches: - if data.loc[rch,"Length"].sum() < threshold: + if data.loc[rch, "Length"].sum() < threshold: short_hw_reaches.append(rch) # CHECK: Are there any short headwter reaches? # --------------------------------------# if len(short_hw_reaches) == 0: print("no more short headwaters to prune") - + # if no more reaches, exit while loop break @@ -214,18 +221,21 @@ def prune_headwaters(data, threshold, network_data): touched.add(i) if list(hw_junctions.values()).count(jun) > 1: - # two short headwaters draining to the same junction + # two short headwaters draining to the same junction # record reach1 as list of segments reach1 = short_hw_reaches[i] - for y, (tw_y, jun_y) in enumerate(hw_junctions.items()): + for y, (tw_y, jun_y) in enumerate(hw_junctions.items()): if jun_y == jun and tw_y != tw and y not in touched: - # the correlary headwater reach draining to the same junction has been found + # the correlary headwater reach draining to the same junction has been found reach2 = short_hw_reaches[y] # trim the shorter of two reaches - if data.loc[reach1,"Length"].sum() <= data.loc[reach2,"Length"].sum(): + if ( + data.loc[reach1, "Length"].sum() + <= data.loc[reach2, "Length"].sum() + ): # trim reach1 hw_prune_list.append(reach1) else: @@ -235,16 +245,17 @@ def prune_headwaters(data, threshold, network_data): if list(hw_junctions.values()).count(jun) == 1: hw_prune_list.append(short_hw_reaches[i]) - print("completed", iter_count, "iterations of headwater pruning") + print("completed", iter_count, "iterations of headwater pruning") iter_count += 1 - data_pruned = data.drop(index = chain.from_iterable(hw_prune_list)) - + data_pruned = data.drop(index=chain.from_iterable(hw_prune_list)) + return data_pruned + def snap_junctions(data, threshold, network_data): - - ''' + + """ This function snaps junctions on oposite ends of a short reach by forcing the lowest order upstream tributary to drain to the reach tail. Short reaches are defined by a user-appointed threshold length. @@ -279,7 +290,7 @@ def snap_junctions(data, threshold, network_data): -------------- - snapped_data (DataFrame): NHD RouteLink data containing no short reaches - ''' + """ # create a copy of the native data for snapping data_snapped = data.copy() @@ -295,23 +306,35 @@ def snap_junctions(data, threshold, network_data): # build list of headwater segments hws = connections.keys() - chain.from_iterable(connections.values()) - + print("finding non-headwater reaches") # create a list of short reaches stranded between junctions - short_reaches = () + short_reaches = () for sublists in tqdm(list(subreaches.values())): for rch in sublists: head = rch[0] - + # if reach is not a headwater - if rconn[head] and data.loc[rch,"Length"].sum() < threshold: + if rconn[head] and data.loc[rch, "Length"].sum() < threshold: short_reaches += tuple(rch) if iter_num > 1: - print("After iteration", iter_num - 1, ",", len(short_reaches), "short reaches remain") + print( + "After iteration", + iter_num - 1, + ",", + len(short_reaches), + "short reaches remain", + ) else: - print("Prior to itteration", iter_num, ",", len(short_reaches), "short reaches exist in the network") + print( + "Prior to itteration", + iter_num, + ",", + len(short_reaches), + "short reaches exist in the network", + ) # check that short reaches exist, if none - terminate process if len(short_reaches) == 0: @@ -321,10 +344,10 @@ def snap_junctions(data, threshold, network_data): for i, rch in enumerate(short_reaches): # identify reach tail (downstream-most) and head (upstream-most) segments - + if type(rch) is tuple: tail = rch[-1] - head = rch[0] + head = rch[0] else: tail = rch head = rch @@ -333,13 +356,13 @@ def snap_junctions(data, threshold, network_data): us_conn = rconn[head] # select the upstream segment to snap - o = data_snapped.loc[us_conn,'order'].tolist() # Strahler order + o = data_snapped.loc[us_conn, "order"].tolist() # Strahler order if min(o) != max(o): # select the segment with lowest Strahler order - rch_to_move = data_snapped.loc[us_conn,'order'].idxmin() + rch_to_move = data_snapped.loc[us_conn, "order"].idxmin() else: # if all segments are same Strahler order, select the shortest - rch_to_move = data_snapped.loc[us_conn,'Length'].idxmin() + rch_to_move = data_snapped.loc[us_conn, "Length"].idxmin() # snap destination is the segment that the reach tail drains to snap_destination = connections[tail] @@ -347,15 +370,16 @@ def snap_junctions(data, threshold, network_data): # if reach tail doesn't have a downstrem connection (it is a network tailwater) # then the snap desination is the ocean if len(snap_destination) == 0: - snap_destination = data_snapped.loc[tail,'to'] + snap_destination = data_snapped.loc[tail, "to"] # update RouteLink data with new tributary destination info - data_snapped.loc[rch_to_move,'to'] = snap_destination + data_snapped.loc[rch_to_move, "to"] = snap_destination iter_num += 1 - + return data_snapped + def len_weighted_av(df, var, weight): """ @@ -372,6 +396,7 @@ def len_weighted_av(df, var, weight): return x + def merge_parameters(to_merge): """ @@ -386,7 +411,7 @@ def merge_parameters(to_merge): data_replace._is_copy = None idx = to_merge.tail(1).index - + data_replace.loc[idx, "Length"] = to_merge.Length.sum() data_replace.loc[idx, "n"] = len_weighted_av(to_merge, "n", "Length") data_replace.loc[idx, "nCC"] = len_weighted_av(to_merge, "nCC", "Length") @@ -399,9 +424,10 @@ def merge_parameters(to_merge): data_replace.loc[idx, "MusK"] = len_weighted_av(to_merge, "MusK", "Length") data_replace.loc[idx, "MusX"] = len_weighted_av(to_merge, "MusX", "Length") data_replace.loc[idx, "ChSlp"] = len_weighted_av(to_merge, "ChSlp", "Length") - + return data_replace + def correct_reach_connections(data_merged): """ @@ -418,6 +444,7 @@ def correct_reach_connections(data_merged): return data_merged + def upstream_merge(data_merged, chop): """ @@ -447,6 +474,7 @@ def upstream_merge(data_merged, chop): return data_merged, chop + def downstream_merge(data_merged, chop, thresh): """ @@ -483,6 +511,7 @@ def downstream_merge(data_merged, chop, thresh): return data_merged, chop + def merge_all(rch, data, chop): """ @@ -516,6 +545,7 @@ def merge_all(rch, data, chop): return data_merged, chop + def update_network_data(data, rch, data_merged, chop, rconn): """ @@ -530,7 +560,7 @@ def update_network_data(data, rch, data_merged, chop, rconn): # drop the segments that disapeared with merger data = data.drop(chop) - + # adjust the segment data for those that remain data.loc[data_merged.index] = data_merged @@ -538,71 +568,77 @@ def update_network_data(data, rch, data_merged, chop, rconn): upstreams = rconn[rch[0]] # upstream connection of the OLD reach head if bool(upstreams): - - data.loc[upstreams, "to"] = data_merged.head(1).index.values[0] # index of NEW reach head + + data.loc[upstreams, "to"] = data_merged.head(1).index.values[ + 0 + ] # index of NEW reach head return data -def qlat_destination_compute(data_native, data_merged, merged_segments, pruned_segments, network_data): - + +def qlat_destination_compute( + data_native, data_merged, merged_segments, pruned_segments, network_data +): + # build a list of all segments that need crosswalking - if bool(list(pruned_segments)): + if bool(list(pruned_segments)): segments = merged_segments + list(pruned_segments) - + else: segments = merged_segments # compute connections using native network data - conn = nhd_network.extract_connections(data_native, network_data["columns"]["downstream"]) + conn = nhd_network.extract_connections( + data_native, network_data["columns"]["downstream"] + ) rconn = nhd_network.reverse_network(conn) - + # initialize a dictionary to store qlat destination nodes for pruned/merged segments qlat_destinations = {} for idx in segments: - # find the segment to recieve qlats from the pruned or merged segment + # find the segment to recieve qlats from the pruned or merged segment if conn[idx]: ds_idx = conn[idx] while bool(ds_idx[0] in data_merged.index) == False: ds_idx = conn[ds_idx[0]] - + elif rconn[idx]: ds_idx = rconn[idx] while bool(ds_idx[0] in data_merged.index) == False: us_idx = conn[ds_idx[0]] - + else: ds_idx = [] # update the qlat destination dict qlat_destinations[str(idx)] = str(ds_idx) - + return qlat_destinations + def segment_merge(data_native, data, network_data, thresh, pruned_segments): - - - - # create a copy of the pruned network dataset, which will be updated with merged data + + # create a copy of the pruned network dataset, which will be updated with merged data data_merged = data.copy() # initialize list to store merged segment IDs merged_segments = [] - + # build connections and reverse connections conn, rconn = network_connections(data, network_data) - + # organize network into reaches subreachable, subreaches, subnets = build_reaches(rconn) - + # loop through each reach in the network for twi, (tw, rchs) in enumerate(subreaches.items(), 1): for rch in rchs: - + rch_len = data.loc[rch].Length.sum() - + ################################################## # orphaned short single segment reaches ################################################## @@ -615,18 +651,14 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): ################################################## # if reach length is shorter than threshold and composed more than one segment if rch_len < thresh and len(data.loc[rch]) > 1: - + # merge ALL reach segments into one chop = [] reach_merged, chop = merge_all(rch, data, chop) # update network with merged reach data data_merged = update_network_data( - data_merged, - rch, - reach_merged, - chop, - rconn + data_merged, rch, reach_merged, chop, rconn ) # update merged_segments list with merged-out segments @@ -649,7 +681,8 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # if shortest segment is the last segment in the reach - conduct an upstream merge. if ( - reach_merged.Length.idxmin() == reach_merged.tail(1).index.values[0] + reach_merged.Length.idxmin() + == reach_merged.tail(1).index.values[0] and reach_merged.Length.min() < thresh ): @@ -662,44 +695,41 @@ def segment_merge(data_native, data, network_data, thresh, pruned_segments): # if shortest segment is NOT the last segment in the reach - conduct a downstream merge if ( - reach_merged.Length.idxmin() != reach_merged.tail(1).index.values[0] + reach_merged.Length.idxmin() + != reach_merged.tail(1).index.values[0] and reach_merged.Length.min() < thresh ): # downstream merge chop = [] - reach_merged, chop = downstream_merge(reach_merged, chop, thresh) + reach_merged, chop = downstream_merge( + reach_merged, chop, thresh + ) # update chop_reach list with merged-out segments chop_reach.extend(chop) - # correct segment connections within reach reach_merged = correct_reach_connections(reach_merged) # update the greater network data set data_merged = update_network_data( - data_merged, - rch, - reach_merged, - chop_reach, - rconn + data_merged, rch, reach_merged, chop_reach, rconn ) # update merged_segments list with merged-out segments merged_segments.extend(chop_reach) - + # create a qlateral destinations dictionary - qlat_destinations = qlat_destination_compute(data_native, - data_merged, - merged_segments, - pruned_segments, - network_data) - + qlat_destinations = qlat_destination_compute( + data_native, data_merged, merged_segments, pruned_segments, network_data + ) + return data_merged, qlat_destinations + def main(): - + # unpack command line arguments args = _handle_args() supernetwork = args.supernetwork @@ -707,7 +737,7 @@ def main(): prune = args.prune snap = args.snap return_original = args.return_original - + # get network data print("Extracting and organizing supernetwork data") data, RouteLink, network_data = get_network_data(supernetwork) @@ -716,128 +746,156 @@ def main(): pruned_segs = [] # prune headwaters if prune and snap: - dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge" - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.shp" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge.json" - + dirname = ( + "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_snap_merge" + ) + filename = ( + "RouteLink_" + + supernetwork + + "_" + + str(threshold) + + "m_prune_snap_merge.shp" + ) + filename_cw = ( + "CrossWalk_" + + supernetwork + + "_" + + str(threshold) + + "m_prune_snap_merge.json" + ) + print("Prune, snap, then merge:") print("pruning headwaters...") data_pruned = prune_headwaters(data, threshold, network_data) - + # identify pruned segments - pruned_segs = list(np.setdiff1d(data.index,data_pruned.index)) - + pruned_segs = list(np.setdiff1d(data.index, data_pruned.index)) + print("snapping junctions...") data_snapped = snap_junctions(data_pruned, threshold, network_data) - + print("merging segments...") data_merged, qlat_destinations = segment_merge( - data, - data_snapped, - network_data, - threshold, - pruned_segs - ) - + data, data_snapped, network_data, threshold, pruned_segs + ) + if snap and not prune: dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge" - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.shp" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.json" - + filename = ( + "RouteLink_" + supernetwork + "_" + str(threshold) + "m_snap_merge.shp" + ) + filename_cw = ( + "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_snap_merge.json" + ) + print("Snap and merge:") print("snapping junctions...") data_snapped = snap_junctions(data, threshold, network_data) - + print("merging segments...") data_merged, qlat_destinations = segment_merge( - data, - data_snapped, - network_data, - threshold, - pruned_segs - ) - + data, data_snapped, network_data, threshold, pruned_segs + ) + if not snap and prune: dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge" - filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.shp" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.json" - + filename = ( + "RouteLink_" + supernetwork + "_" + str(threshold) + "m_prune_merge.shp" + ) + filename_cw = ( + "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_prune_merge.json" + ) + print("Prune and merge:") print("snapping junctions...") data_snapped = snap_junctions(data, threshold, network_data) - + print("merging segments...") data_merged, qlat_destinations = segment_merge( - data, - data_snapped, - network_data, - threshold, - pruned_segs - ) - + data, data_snapped, network_data, threshold, pruned_segs + ) + if not snap and not prune: dirname = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge" filename = "RouteLink_" + supernetwork + "_" + str(threshold) + "m_merge.nc" - filename_cw = "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.json" + filename_cw = ( + "CrossWalk_" + supernetwork + "_" + str(threshold) + "m_merge.json" + ) print("Just merge:") print("merging segments...") data_merged, qlat_destinations = segment_merge( - data, - data, - network_data, - threshold, - pruned_segs - ) - + data, data, network_data, threshold, pruned_segs + ) + # update RouteLink data - RouteLink_edit = RouteLink.loc[data_merged.index.values] + RouteLink_edit = RouteLink.loc[data_merged.index.values] - for (columnName, columnData) in data_merged.iteritems(): - RouteLink_edit.loc[:,columnName] = columnData + for (columnName, columnData) in data_merged.iteritems(): + RouteLink_edit.loc[:, columnName] = columnData for idx in RouteLink_edit.index: - if RouteLink_edit.loc[idx,'to'] < 0: - RouteLink_edit.loc[idx,'to'] = 0 - + if RouteLink_edit.loc[idx, "to"] < 0: + RouteLink_edit.loc[idx, "to"] = 0 + # convert RouteLink to geodataframe - RouteLink_edit = gpd.GeoDataFrame(RouteLink_edit, geometry=gpd.points_from_xy(RouteLink_edit.lon, RouteLink_edit.lat)) - + RouteLink_edit = gpd.GeoDataFrame( + RouteLink_edit, + geometry=gpd.points_from_xy(RouteLink_edit.lon, RouteLink_edit.lat), + ) + # export merged data - print("exporting RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) - + print( + "exporting RouteLink file:", + filename, + "to", + os.path.join(root, "test", "input", "geo", "Channels"), + ) + dir_path = os.path.join(root, "test", "input", "geo", "Channels", dirname) if not os.path.isdir(dir_path): os.mkdir(dir_path) - + # save RouteLink data as shapefile - RouteLink_edit = RouteLink_edit.drop(columns = ['time', 'gages']) - RouteLink_edit.to_file(os.path.join(dir_path,filename)) - + RouteLink_edit = RouteLink_edit.drop(columns=["time", "gages"]) + RouteLink_edit.to_file(os.path.join(dir_path, filename)) + # save cross walk as json - print("exporting CrossWalk file:", filename_cw, "to", os.path.join(root, "test", "input", "geo", "Channels")) - with open(os.path.join(dir_path, filename_cw), "w") as outfile: - json.dump(qlat_destinations, outfile) - + print( + "exporting CrossWalk file:", + filename_cw, + "to", + os.path.join(root, "test", "input", "geo", "Channels"), + ) + with open(os.path.join(dir_path, filename_cw), "w") as outfile: + json.dump(qlat_destinations, outfile) + # export original data if return_original: dirname = "RouteLink_" + supernetwork filename = "RouteLink_" + supernetwork + ".shp" - print("exporting unmodified RouteLink file:", filename, "to", os.path.join(root, "test", "input", "geo", "Channels")) - + print( + "exporting unmodified RouteLink file:", + filename, + "to", + os.path.join(root, "test", "input", "geo", "Channels"), + ) + dir_path = os.path.join(root, "test", "input", "geo", "Channels", dirname) if not os.path.isdir(dir_path): os.mkdir(dir_path) - + RouteLink_domain = RouteLink.loc[data.index.values] - RouteLink_domain = gpd.GeoDataFrame(RouteLink_domain, geometry=gpd.points_from_xy(RouteLink_domain.lon, RouteLink_domain.lat)) + RouteLink_domain = gpd.GeoDataFrame( + RouteLink_domain, + geometry=gpd.points_from_xy(RouteLink_domain.lon, RouteLink_domain.lat), + ) + + RouteLink_domain = RouteLink_domain.drop(columns=["time", "gages"]) + RouteLink_domain.to_file(os.path.join(dir_path, filename)) - RouteLink_domain = RouteLink_domain.drop(columns = ['time', 'gages']) - RouteLink_domain.to_file(os.path.join(dir_path,filename)) - print("Number of segments in modified RouteLink:", len(RouteLink_edit)) print("Number of segments in original RouteLink:", len(RouteLink_domain)) - - + + if __name__ == "__main__": - main() - + main() diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 478f3b5db..4527786bd 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -6,6 +6,7 @@ import troute.nhd_io as nhd_io import troute.nhd_network as nhd_network + def set_supernetwork_parameters( supernetwork="", geo_input_folder=None, verbose=True, debuglevel=0 ): @@ -300,10 +301,7 @@ def set_supernetwork_parameters( { "title_string": "Cape Fear River Basin, NC", # overwrites other title... "mask_file_path": os.path.join( - geo_input_folder, - "Channels", - "masks", - "CapeFear_FULL_RES.txt", + geo_input_folder, "Channels", "masks", "CapeFear_FULL_RES.txt", ), "mask_driver_string": "csv", "mask_layer_string": "", @@ -312,7 +310,7 @@ def set_supernetwork_parameters( } ) return rv - + elif supernetwork == "Florence_FULL_RES": rv = set_supernetwork_parameters( supernetwork="CONUS_FULL_RES_v20", geo_input_folder=geo_input_folder @@ -321,10 +319,7 @@ def set_supernetwork_parameters( { "title_string": "Hurricane Florence Domain, near Durham NC", # overwrites other title... "mask_file_path": os.path.join( - geo_input_folder, - "Channels", - "masks", - "Florence_FULL_RES.txt", + geo_input_folder, "Channels", "masks", "Florence_FULL_RES.txt", ), "mask_driver_string": "csv", "mask_layer_string": "", @@ -333,7 +328,7 @@ def set_supernetwork_parameters( } ) return rv - + elif supernetwork == "CONUS_FULL_RES_v20": ROUTELINK = "RouteLink_NHDPLUS" @@ -397,9 +392,10 @@ def reverse_dict(d): """ return {v: k for k, v in d.items()} + def build_connections(supernetwork_parameters, dt): # TODO: Remove the dependence on dt in this function - + cols = supernetwork_parameters["columns"] param_df = nhd_io.read(supernetwork_parameters["geo_file_path"]) @@ -433,6 +429,7 @@ def build_connections(supernetwork_parameters, dt): # datasub = data[['dt', 'bw', 'tw', 'twcc', 'dx', 'n', 'ncc', 'cs', 's0']] return connections, wbodies, param_df + def organize_independent_networks(connections): rconn = nhd_network.reverse_network(connections) @@ -447,7 +444,9 @@ def organize_independent_networks(connections): def build_channel_initial_state(restart_parameters, channel_index=None): - wrf_hydro_channel_restart_file = restart_parameters.get("wrf_hydro_channel_restart_file",None) + wrf_hydro_channel_restart_file = restart_parameters.get( + "wrf_hydro_channel_restart_file", None + ) if wrf_hydro_channel_restart_file: @@ -475,9 +474,13 @@ def build_qlateral_array(forcing_parameters, connection_keys): qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) qlat_input_file = forcing_parameters.get("qlat_input_file", None) if qlat_input_folder: - qlat_file_pattern_filter = forcing_parameters.get("qlat_file_pattern_filter","*CHRT_OUT*") - qlat_file_index_col = forcing_parameters.get("qlat_file_index_col","feature_id") - qlat_file_value_col = forcing_parameters.get("qlat_file_value_col","q_lateral") + qlat_file_pattern_filter = forcing_parameters.get( + "qlat_file_pattern_filter", "*CHRT_OUT*" + ) + qlat_file_index_col = forcing_parameters.get( + "qlat_file_index_col", "feature_id" + ) + qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", "q_lateral") qlat_files = glob.glob(qlat_input_folder + qlat_file_pattern_filter) qlat_df = nhd_io.get_ql_from_wrf_hydro( qlat_files=qlat_files, @@ -502,4 +505,3 @@ def build_qlateral_array(forcing_parameters, connection_keys): ) return qlat_df - diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index f0fa15d52..21657bb6d 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -352,12 +352,17 @@ def compute_nhd_routing_v02( return results -def main(): +def _input_handler(): + args = _handle_args() custom_input_file = args.custom_input_file supernetwork_parameters = None - waterbody_parameters = None + waterbody_parameters = {} + forcing_parameters = {} + restart_parameters = {} + output_parameters = {} + run_parameters = {} if custom_input_file: ( @@ -379,168 +384,184 @@ def main(): # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) # else: # TODO: uncomment custominput file + else: - dt = args.dt - nts = args.nts - qts_subdivisions = args.qts_subdivisions - debuglevel = -1 * args.debuglevel - verbose = args.verbose - showtiming = args.showtiming - supernetwork = args.supernetwork - break_network_at_waterbodies = args.break_network_at_waterbodies - csv_output_folder = args.csv_output_folder - assume_short_ts = args.assume_short_ts - test_folder = pathlib.Path(root, "test") - geo_input_folder = test_folder.joinpath("input", "geo") + run_parameters["assume_short_ts"] = args.assume_short_ts + run_parameters["parallel_compute_method"] = args.parallel_compute_method + run_parameters["cpu_pool"] = args.cpu_pool + run_parameters["showtiming"] = args.showtiming - parallel_compute_method = args.parallel_compute_method + run_parameters["debuglevel"] = debuglevel = -1 * args.debuglevel + run_parameters["verbose"] = verbose = args.verbose - cpu_pool = args.cpu_pool - compute_method = args.compute_method - wrf_hydro_channel_restart_file = args.wrf_hydro_channel_restart_file - wrf_hydro_channel_ID_crosswalk_file = args.wrf_hydro_channel_ID_crosswalk_file - wrf_hydro_channel_ID_crosswalk_file_field_name = ( - args.wrf_hydro_channel_ID_crosswalk_file_field_name - ) - wrf_hydro_channel_restart_upstream_flow_field_name = ( - args.wrf_hydro_channel_restart_upstream_flow_field_name - ) - wrf_hydro_channel_restart_downstream_flow_field_name = ( - args.wrf_hydro_channel_restart_downstream_flow_field_name - ) - wrf_hydro_channel_restart_depth_flow_field_name = ( - args.wrf_hydro_channel_restart_depth_flow_field_name - ) - qlat_const = float(args.qlat_const) - qlat_input_folder = args.qlat_input_folder - qlat_input_file = args.qlat_input_file - qlat_file_pattern_filter = args.qlat_file_pattern_filter - qlat_file_index_col = args.qlat_file_index_col - qlat_file_value_col = args.qlat_file_value_col - # print(forcing_parameters,qlat_const) - # TODO: Make these commandline args - """##NHD Subset (Brazos/Lower Colorado)""" - # supernetwork = 'Brazos_LowerColorado_Named_Streams' - # supernetwork = 'Brazos_LowerColorado_ge5' - # supernetwork = 'Pocono_TEST1' - """##NHD CONUS order 5 and greater""" - # supernetwork = 'CONUS_ge5' - """These are large -- be careful""" - # supernetwork = 'Mainstems_CONUS' - # supernetwork = 'CONUS_FULL_RES_v20' - # supernetwork = 'CONUS_Named_Streams' #create a subset of the full resolution by reading the GNIS field - # supernetwork = 'CONUS_Named_combined' #process the Named streams through the Full-Res paths to join the many hanging reaches - - run_pocono2_test = args.run_pocono2_test - run_pocono1_test = args.run_pocono1_test - - if run_pocono2_test: - if verbose: - print("running test case for Pocono_TEST2 domain") - # Overwrite the following test defaults - supernetwork = "Pocono_TEST2" - break_network_at_waterbodies = False - qts_subdivisions = 1 # change qts_subdivisions = 1 as default - dt = 300 / qts_subdivisions - nts = 144 * qts_subdivisions - csv_output = {"csv_output_folder": os.path.join(root, "test", "output", "text")} - nc_output_folder = os.path.join(root, "test", "output", "text") - # test 1. Take lateral flow from re-formatted wrf-hydro output from Pocono Basin simulation - qlat_input_file = os.path.join( - root, r"test/input/geo/PoconoSampleData2/Pocono_ql_testsamp1_nwm_mc.csv" - ) + test_folder = pathlib.Path(root, "test") + geo_input_folder = test_folder.joinpath("input", "geo") - elif run_pocono1_test: - # NOTE: The test case for the Pocono basin was derived from this - # resource on HydroShare, developed by aaraney and sourced from the - # wrf_hydro_nwm_public repository on GitHub - # see: https://www.hydroshare.org/resource/03ca354200e540018d44183598890448/ - # By downloading aaraney's docker job scheduler repo from GitHub, one can - # execute the WRF-Hydro model that generated the test results - # see: https://github.com/aaraney/NWM-Dockerized-Job-Scheduler - if verbose: - print("running test case for Pocono_TEST1 domain") - # Overwrite the following test defaults - - NWM_test_path = os.path.join( - root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/" - ) - # lakeparm_file = os.path.join( - # NWM_test_path, "primary_domain", "DOMAIN", "LAKEPARM.nc", - # ) - routelink_file = os.path.join( - NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", - ) - time_string = "2017-12-31_06-00_DOMAIN1" - wrf_hydro_restart_file = os.path.join( - NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string - ) - supernetwork_parameters = { - "title_string": "Custom Input Example (using Pocono Test Example datafile)", - "geo_file_path": routelink_file, - "columns": { - "key": "link", - "downstream": "to", - "dx": "Length", - "n": "n", # TODO: rename to `manningn` - "ncc": "nCC", # TODO: rename to `mannningncc` - "s0": "So", # TODO: rename to `bedslope` - "bw": "BtmWdth", # TODO: rename to `bottomwidth` - "waterbody": "NHDWaterbodyComID", - "tw": "TopWdth", # TODO: rename to `topwidth` - "twcc": "TopWdthCC", # TODO: rename to `topwidthcc` - "musk": "MusK", - "musx": "MusX", - "cs": "ChSlp", # TODO: rename to `sideslope` - }, - "waterbody_null_code": -9999, - "terminal_code": 0, - "driver_string": "NetCDF", - "layer_string": 0, - } - # waterbody_parameters = { - # "level_pool": { - # "level_pool_waterbody_parameter_file_path": lakeparm_file, - # "level_pool_waterbody_id": "lake_id", - # "level_pool_waterbody_area": "LkArea", - # "level_pool_weir_elevation": "WeirE", - # "level_pool_waterbody_max_elevation": "LkMxE", - # "level_pool_outfall_weir_coefficient": "WeirC", - # "level_pool_outfall_weir_length": "WeirL", - # "level_pool_overall_dam_length": "DamL", - # "level_pool_orifice_elevation": "OrificeE", - # "level_pool_orifice_coefficient": "OrificeC", - # "level_pool_orifice_area": "OrificeA", - # } - # } - # break_network_at_waterbodies = True - qts_subdivisions = 12 - dt = 3600 / qts_subdivisions - nts = 24 * qts_subdivisions - csv_output = None - nc_output_folder = None - # build a time string to specify input date - wrf_hydro_channel_restart_file = wrf_hydro_restart_file - wrf_hydro_channel_ID_crosswalk_file = routelink_file - wrf_hydro_channel_ID_crosswalk_file_field_name = "link" - wrf_hydro_channel_restart_upstream_flow_field_name = "qlink1" - wrf_hydro_channel_restart_downstream_flow_field_name = "qlink2" - wrf_hydro_channel_restart_depth_flow_field_name = "hlink" - # wrf_hydro_waterbody_restart_file = wrf_hydro_restart_file - # wrf_hydro_waterbody_ID_crosswalk_file = lakeparm_file - # wrf_hydro_waterbody_ID_crosswalk_file_field_name = "lake_id" - # wrf_hydro_waterbody_crosswalk_filter_file = routelink_file - # wrf_hydro_waterbody_crosswalk_filter_file_field_name = "NHDWaterbodyComID" - # wrf_hydro_waterbody_crosswalk_file_output_order_field= "AscendingIndex" - qlat_input_folder = os.path.join( - root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/" - ) - qlat_file_pattern_filter = "/*.CHRTOUT_DOMAIN1" - qlat_file_index_col = "feature_id" - qlat_file_value_col = "q_lateral" + run_pocono2_test = args.run_pocono2_test + run_pocono1_test = args.run_pocono1_test + + if run_pocono2_test: + if verbose: + print("running test case for Pocono_TEST2 domain") + # Overwrite the following test defaults + supernetwork = "Pocono_TEST2" + waterbody_parameters["break_network_at_waterbodies"] = False + run_parameters["qts_subdivisions"] = qts_subdivisions = 1 + run_parameters["dt"] = 300 / qts_subdivisions + run_parameters["nts"] = 144 * qts_subdivisions + output_parameters["csv_output"] = { + "csv_output_folder": os.path.join(root, "test", "output", "text") + } + output_parameters["nc_output_folder"] = os.path.join( + root, "test", "output", "text" + ) + # test 1. Take lateral flow from re-formatted wrf-hydro output from Pocono Basin simulation + forcing_parameters["qlat_input_file"] = os.path.join( + root, r"test/input/geo/PoconoSampleData2/Pocono_ql_testsamp1_nwm_mc.csv" + ) - # STEP 0.5: Obtain Supernetwork Parameters for test cases - if not supernetwork_parameters: + elif run_pocono1_test: + # NOTE: The test case for the Pocono basin was derived from this + # resource on HydroShare, developed by aaraney and sourced from the + # wrf_hydro_nwm_public repository on GitHub + # see: https://www.hydroshare.org/resource/03ca354200e540018d44183598890448/ + # By downloading aaraney's docker job scheduler repo from GitHub, one can + # execute the WRF-Hydro model that generated the test results + # see: https://github.com/aaraney/NWM-Dockerized-Job-Scheduler + if verbose: + print("running test case for Pocono_TEST1 domain") + # Overwrite the following test defaults + + NWM_test_path = os.path.join( + root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/" + ) + # lakeparm_file = os.path.join( + # NWM_test_path, "primary_domain", "DOMAIN", "LAKEPARM.nc", + # ) + routelink_file = os.path.join( + NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", + ) + time_string = "2017-12-31_06-00_DOMAIN1" + restart_parameters["wrf_hydro_restart_file"] = os.path.join( + NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string + ) + supernetwork_parameters = { + "title_string": "Custom Input Example (using Pocono Test Example datafile)", + "geo_file_path": routelink_file, + "columns": { + "key": "link", + "downstream": "to", + "dx": "Length", + "n": "n", # TODO: rename to `manningn` + "ncc": "nCC", # TODO: rename to `mannningncc` + "s0": "So", # TODO: rename to `bedslope` + "bw": "BtmWdth", # TODO: rename to `bottomwidth` + "waterbody": "NHDWaterbodyComID", + "tw": "TopWdth", # TODO: rename to `topwidth` + "twcc": "TopWdthCC", # TODO: rename to `topwidthcc` + "musk": "MusK", + "musx": "MusX", + "cs": "ChSlp", # TODO: rename to `sideslope` + }, + "waterbody_null_code": -9999, + "terminal_code": 0, + "driver_string": "NetCDF", + "layer_string": 0, + } + # waterbody_parameters = { + # "level_pool": { + # "level_pool_waterbody_parameter_file_path": lakeparm_file, + # "level_pool_waterbody_id": "lake_id", + # "level_pool_waterbody_area": "LkArea", + # "level_pool_weir_elevation": "WeirE", + # "level_pool_waterbody_max_elevation": "LkMxE", + # "level_pool_outfall_weir_coefficient": "WeirC", + # "level_pool_outfall_weir_length": "WeirL", + # "level_pool_overall_dam_length": "DamL", + # "level_pool_orifice_elevation": "OrificeE", + # "level_pool_orifice_coefficient": "OrificeC", + # "level_pool_orifice_area": "OrificeA", + # } + # } + # break_network_at_waterbodies = True + run_parameters["qts_subdivisions"] = 12 + run_parameters["dt"] = 3600 / qts_subdivisions + run_parameters["nts"] = 24 * qts_subdivisions + output_parameters["csv_output"] = None + output_parameters["nc_output_folder"] = None + # build a time string to specify input date + restart_parameters[ + "wrf_hydro_channel_restart_file" + ] = wrf_hydro_restart_file + restart_parameters["wrf_hydro_channel_ID_crosswalk_file"] = routelink_file + restart_parameters[ + "wrf_hydro_channel_ID_crosswalk_file_field_name" + ] = "link" + restart_parameters[ + "wrf_hydro_channel_restart_upstream_flow_field_name" + ] = "qlink1" + restart_parameters[ + "wrf_hydro_channel_restart_downstream_flow_field_name" + ] = "qlink2" + restart_parameters[ + "wrf_hydro_channel_restart_depth_flow_field_name" + ] = "hlink" + # restart_parameters["wrf_hydro_waterbody_restart_file"] = wrf_hydro_restart_file + # restart_parameters["wrf_hydro_waterbody_ID_crosswalk_file"] = lakeparm_file + # restart_parameters["wrf_hydro_waterbody_ID_crosswalk_file_field_name"] = "lake_id" + # restart_parameters["wrf_hydro_waterbody_crosswalk_filter_file"] = routelink_file + # restart_parameters["wrf_hydro_waterbody_crosswalk_filter_file_field_name"] = "NHDWaterbodyComID" + # restart_parameters["wrf_hydro_waterbody_crosswalk_file_output_order_field= "AscendingIndex" + forcing_parameters["qlat_input_folder"] = os.path.join( + root, + "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", + ) + forcing_parameters["qlat_file_pattern_filter"] = "/*.CHRTOUT_DOMAIN1" + forcing_parameters["qlat_file_index_col"] = "feature_id" + forcing_parameters["qlat_file_value_col"] = "q_lateral" + + else: + run_parameters["dt"] = args.dt + run_parameters["nts"] = args.nts + run_parameters["qts_subdivisions"] = args.qts_subdivisions + run_parameters["compute_method"] = args.compute_method + + waterbody_parameters[ + "break_network_at_waterbodies" + ] = args.break_network_at_waterbodies + output_parameters["csv_output_folder"] = args.csv_output_folder + + restart_parameters[ + "wrf_hydro_channel_restart_file" + ] = args.wrf_hydro_channel_restart_file + restart_parameters[ + "wrf_hydro_channel_ID_crosswalk_file" + ] = args.wrf_hydro_channel_ID_crosswalk_file + restart_parameters[ + "wrf_hydro_channel_ID_crosswalk_file_field_name" + ] = args.wrf_hydro_channel_ID_crosswalk_file_field_name + restart_parameters[ + "wrf_hydro_channel_restart_upstream_flow_field_name" + ] = args.wrf_hydro_channel_restart_upstream_flow_field_name + restart_parameters[ + "wrf_hydro_channel_restart_downstream_flow_field_name" + ] = args.wrf_hydro_channel_restart_downstream_flow_field_name + restart_parameters[ + "wrf_hydro_channel_restart_depth_flow_field_name" + ] = args.wrf_hydro_channel_restart_depth_flow_field_name + + forcing_parameters["qlat_const"] = float(args.qlat_const) + forcing_parameters["qlat_input_folder"] = args.qlat_input_folder + forcing_parameters["qlat_input_file"] = args.qlat_input_file + forcing_parameters[ + "qlat_file_pattern_filter" + ] = args.qlat_file_pattern_filter + forcing_parameters["qlat_file_index_col"] = args.qlat_file_index_col + forcing_parameters["qlat_file_value_col"] = args.qlat_file_value_col + + supernetwork = args.supernetwork + + # STEP 0.5: Obtain Supernetwork Parameters for test cases supernetwork_parameters = nnu.set_supernetwork_parameters( supernetwork=supernetwork, geo_input_folder=geo_input_folder, @@ -548,6 +569,33 @@ def main(): debuglevel=debuglevel, ) + return ( + supernetwork_parameters, + waterbody_parameters, + forcing_parameters, + restart_parameters, + output_parameters, + run_parameters, + ) + + +def main(): + + ( + supernetwork_parameters, + waterbody_parameters, + forcing_parameters, + restart_parameters, + output_parameters, + run_parameters, + ) = _input_handler() + + dt = run_parameters.get("dt", None) + nts = run_parameters.get("nts", None) + verbose = run_parameters.get("verbose", None) + showtiming = run_parameters.get("showtiming", None) + debuglevel = run_parameters.get("debuglevel", 0) + if verbose: print("creating supernetwork connections set") if showtiming: @@ -609,7 +657,7 @@ def main(): if verbose: print(f"executing routing computation ...") - if compute_method == "standard cython compute network": + if run_parameters.get("compute_method", None) == "standard cython compute network": compute_func = mc_reach.compute_network else: compute_func = mc_reach.compute_network @@ -617,17 +665,18 @@ def main(): results = compute_nhd_routing_v02( reaches_bytw, compute_func, - parallel_compute_method, - cpu_pool, - nts, - qts_subdivisions, + run_parameters.get("parallel_compute_method", None), + run_parameters.get("cpu_pool", None), + run_parameters.get("nts", 1), + run_parameters.get("qts_subdivisions", 1), independent_networks, param_df, qlats, q0, - assume_short_ts, + run_parameters.get("assume_short_ts", False), ) + csv_output_folder = output_parameters.get("csv_output_folder", None) if (debuglevel <= -1) or csv_output_folder: qvd_columns = pd.MultiIndex.from_product( [range(nts), ["q", "v", "d"]] From 2b8dfa98f7e4fb65f4382b63c7f0d1de3e50f5f0 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 22:28:18 -0600 Subject: [PATCH 36/54] fix q0 indent --- src/python_framework_v02/troute/nhd_network_utilities_v02.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 4527786bd..b31fdaf58 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -466,7 +466,7 @@ def build_channel_initial_state(restart_parameters, channel_index=None): 0, index=channel_index, columns=["qu0", "qd0", "h0"], dtype="float32", ) - return q0 + return q0 def build_qlateral_array(forcing_parameters, connection_keys): From 0603993f680a8c7abc6cea4588e40bdc2c005115 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 22:29:05 -0600 Subject: [PATCH 37/54] import glob --- src/python_framework_v02/troute/nhd_network_utilities_v02.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index b31fdaf58..503065d8e 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -1,6 +1,7 @@ import json import os +import glob import pandas as pd from functools import partial import troute.nhd_io as nhd_io From 0030a7cda3396b7a328aa6925931d43c8ac2aa03 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Mon, 14 Dec 2020 22:29:30 -0600 Subject: [PATCH 38/54] fix test-full-pocono --- .../troute/nhd_network_utilities_v02.py | 6 +++-- .../compute_nhd_routing_SingleSeg_v02.py | 23 +++++++++---------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 503065d8e..4078a1751 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -470,7 +470,8 @@ def build_channel_initial_state(restart_parameters, channel_index=None): return q0 -def build_qlateral_array(forcing_parameters, connection_keys): +def build_qlateral_array(forcing_parameters, connections_keys, nts): + # TODO: set default/optional arguments qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) qlat_input_file = forcing_parameters.get("qlat_input_file", None) @@ -501,8 +502,9 @@ def build_qlateral_array(forcing_parameters, connection_keys): qlat_df = nhd_io.get_ql_from_csv(qlat_input_file) else: + qlat_const = forcing_parameters.get("qlat_const", 0) qlat_df = pd.DataFrame( - qlat_const, index=connections.keys(), columns=range(nts), dtype="float32", + qlat_const, index=connections_keys, columns=range(nts), dtype="float32", ) return qlat_df diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 21657bb6d..1e351729b 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -442,7 +442,7 @@ def _input_handler(): NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", ) time_string = "2017-12-31_06-00_DOMAIN1" - restart_parameters["wrf_hydro_restart_file"] = os.path.join( + wrf_hydro_restart_file = os.path.join( NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string ) supernetwork_parameters = { @@ -484,15 +484,13 @@ def _input_handler(): # } # } # break_network_at_waterbodies = True - run_parameters["qts_subdivisions"] = 12 + run_parameters["qts_subdivisions"] = qts_subdivisions = 12 run_parameters["dt"] = 3600 / qts_subdivisions run_parameters["nts"] = 24 * qts_subdivisions output_parameters["csv_output"] = None output_parameters["nc_output_folder"] = None # build a time string to specify input date - restart_parameters[ - "wrf_hydro_channel_restart_file" - ] = wrf_hydro_restart_file + restart_parameters["wrf_hydro_channel_restart_file"] = wrf_hydro_restart_file restart_parameters["wrf_hydro_channel_ID_crosswalk_file"] = routelink_file restart_parameters[ "wrf_hydro_channel_ID_crosswalk_file_field_name" @@ -562,12 +560,13 @@ def _input_handler(): supernetwork = args.supernetwork # STEP 0.5: Obtain Supernetwork Parameters for test cases - supernetwork_parameters = nnu.set_supernetwork_parameters( - supernetwork=supernetwork, - geo_input_folder=geo_input_folder, - verbose=False, - debuglevel=debuglevel, - ) + if not supernetwork_parameters: + supernetwork_parameters = nnu.set_supernetwork_parameters( + supernetwork=supernetwork, + geo_input_folder=geo_input_folder, + verbose=False, + debuglevel=debuglevel, + ) return ( supernetwork_parameters, @@ -644,7 +643,7 @@ def main(): if verbose: print("creating qlateral array ...") - qlats = nnu.build_qlateral_array(forcing_parameters, connections.keys()) + qlats = nnu.build_qlateral_array(forcing_parameters, connections.keys(), nts) if verbose: print("qlateral array complete") From 6851f95e09271b3f63e0e548bdd8a786ed295feb Mon Sep 17 00:00:00 2001 From: James Halgren Date: Wed, 16 Dec 2020 16:02:52 -0600 Subject: [PATCH 39/54] update yaml/json CustomInput files for v02 --- .../compute_nhd_routing_SingleSeg_v02.py | 14 +----- test/input/json/CustomInput.json | 32 ++++++------ test/input/yaml/CustomInput.yaml | 49 ++++++++----------- 3 files changed, 39 insertions(+), 56 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 1e351729b..cf6dea9c3 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -372,18 +372,8 @@ def _input_handler(): restart_parameters, output_parameters, run_parameters, - ) = nio.read_custom_input(custom_input_file) - # TODO: uncomment custominput file - # qlat_const = forcing_parameters.get("qlat_const", None) - # qlat_input_file = forcing_parameters.get("qlat_input_file", None) - # qlat_input_folder = forcing_parameters.get("qlat_input_folder", None) - # qlat_file_pattern_filter = forcing_parameters.get( - # "qlat_file_pattern_filter", None - # ) - # qlat_file_index_col = forcing_parameters.get("qlat_file_index_col", None) - # qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", None) - # else: - # TODO: uncomment custominput file + ) = nhd_io.read_custom_input(custom_input_file) + run_parameters["debuglevel"] *= -1 else: run_parameters["assume_short_ts"] = args.assume_short_ts diff --git a/test/input/json/CustomInput.json b/test/input/json/CustomInput.json index 47cfbcc3c..c95834bc2 100644 --- a/test/input/json/CustomInput.json +++ b/test/input/json/CustomInput.json @@ -4,7 +4,7 @@ "parallel_compute": true, "verbose": true, "showtiming": true, - "debuglevel": 0, + "debuglevel": 1, "break_network_at_waterbodies": true, "assume_short_ts": true, "qts_subdivisions": 12, @@ -29,21 +29,23 @@ "mask_file_path": "../../test/input/geo/Channels/masks/Pocono_mask.csv", "mask_layer_string": "", "mask_driver_string": "csv", - "mask_key_col": 0, - "key_col": 16, - "downstream_col": 22, - "length_col": 3, - "manningn_col": 18, - "manningncc_col": 19, - "slope_col": 8, - "bottomwidth_col": 0, - "waterbody_col": 6, + "mask_key": 0, + "columns": { + "key": "link", + "downstream": "to", + "dx": "Length", + "n": "n", + "ncc": "nCC", + "s0": "So", + "bw": "BtmWdth", + "tw": "TopWdth", + "twcc": "TopWdthCC", + "waterbody": "NHDWaterbodyComID", + "musk": "MusK", + "musx": "MusX", + "cs": "ChSlp" + }, "waterbody_null_code": -9999, - "topwidth_col": 9, - "topwidthcc_col": 10, - "MusK_col": 4, - "MusX_col": 5, - "ChSlp_col": 1, "terminal_code": 0, "driver_string": "NetCDF", "layer_string": 0 diff --git a/test/input/yaml/CustomInput.yaml b/test/input/yaml/CustomInput.yaml index 3da863d76..3e7ea88b4 100644 --- a/test/input/yaml/CustomInput.yaml +++ b/test/input/yaml/CustomInput.yaml @@ -4,7 +4,7 @@ run_parameters: parallel_compute: false verbose: true showtiming: true - debuglevel: 0 + debuglevel: 1 break_network_at_waterbodies: true assume_short_ts: true qts_subdivisions: 12 @@ -32,39 +32,30 @@ output_parameters: supernetwork_parameters: title_string: Custom Input Example (using Pocono Test Example datafile) geo_file_path: "../../test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/primary_domain/DOMAIN/Route_Link.nc" - key_col: 16 - downstream_col: 22 - length_col: 3 - manningn_col: 18 - manningncc_col: 19 - slope_col: 8 - bottomwidth_col: 0 - waterbody_col: 6 + mask_file_path: "../../test/input/geo/Channels/masks/Pocono_mask.csv" + mask_layer_string: "" + mask_driver_string: "csv" + mask_key: 0 + columns: + key: "link" + downstream: "to" + dx: "Length" + n: "n" # TODO: rename to `manningn` + ncc: "nCC" # TODO: rename to `mannningncc` + s0: "So" # TODO: rename to `bedslope` + bw: "BtmWdth" # TODO: rename to `bottomwidth` + waterbody: "NHDWaterbodyComID" + tw: "TopWdth" # TODO: rename to `topwidth` + twcc: "TopWdthCC" # TODO: rename to `topwidthcc` + musk: "MusK" + musx: "MusX" + cs: "ChSlp" # TODO: rename to `sideslope` waterbody_null_code: -9999 - topwidth_col: 9 - topwidthcc_col: 10 - MusK_col: 4 - MusX_col: 5 - ChSlp_col: 1 terminal_code: 0 driver_string: NetCDF layer_string: 0 - # "link", - # "to", - # "Length", - # "n", - # "nCC", - # "So", - # "BtmWdth", - # "TopWdth", - # "TopWdthCC", - # "NHDWaterbodyComID", - # "MusK", - # "MusX", - # "ChSlp", - -#waterboy parameters and assignments from lake parm file +#waterbody parameters and assignments from lake parm file waterbody_parameters: level_pool: #WRF-Hydro lake parm file From f411b046c4a390a200bfb733241bc0449d2266c1 Mon Sep 17 00:00:00 2001 From: Adam Wlostowski <66840445+awlostowski-noaa@users.noreply.github.com> Date: Wed, 16 Dec 2020 17:06:48 -0500 Subject: [PATCH 40/54] nhd test file (#15) * create new build_tests.py file to contain v02 routing test cases * note locations of test material to be moved * functions to build test cases and check parity * adjustments to accommodate testing capability with new build_tests.py Co-authored-by: jameshalgren <53343824+jameshalgren@users.noreply.github.com> --- src/python_routing_v02/build_tests.py | 183 +++++++++++++++++ .../compute_nhd_routing_SingleSeg_v02.py | 188 +++++------------- 2 files changed, 235 insertions(+), 136 deletions(-) create mode 100644 src/python_routing_v02/build_tests.py diff --git a/src/python_routing_v02/build_tests.py b/src/python_routing_v02/build_tests.py new file mode 100644 index 000000000..3749d304f --- /dev/null +++ b/src/python_routing_v02/build_tests.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# coding: utf-8 + +"""NHD Network test cases + +Test v02 routing on specific test cases + +""" +## Parallel execution +import os +import sys +import time +import numpy as np +import argparse +import pathlib +import glob +import pandas as pd +from functools import partial +from joblib import delayed, Parallel +from itertools import chain, islice +from operator import itemgetter + +## network and reach utilities +import compute_nhd_routing_SingleSeg_v02 as nhd_compute +import troute.nhd_network_utilities_v02 as nnu +import mc_reach +import troute.nhd_network as nhd_network +import troute.nhd_io as nhd_io + +ENV_IS_CL = False +if ENV_IS_CL: + root = pathlib.Path("/", "content", "t-route") +elif not ENV_IS_CL: + root = pathlib.Path("../..").resolve() + +def build_test_parameters(test_name, + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters + ): + + if test_name == "pocono1": + + print("running test case for Pocono_TEST1 domain - NO RESERVOIRS") + + # File path to WRF Hydro data + NWM_test_path = os.path.join( + root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/" + ) + + # Simulation domain RouteLink file + routelink_file = os.path.join( + NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", + ) + + # Speficify WRF hydro restart file, name and destination + time_string = "2017-12-31_06-00_DOMAIN1" + wrf_hydro_restart_file = os.path.join( + NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string + ) + + # specify supernetwork parameters + supernetwork_parameters = { + "title_string": "Custom Input Example (using Pocono Test Example datafile)", + "geo_file_path": routelink_file, + "columns": { + "key": "link", + "downstream": "to", + "dx": "Length", + "n": "n", # TODO: rename to `manningn` + "ncc": "nCC", # TODO: rename to `mannningncc` + "s0": "So", # TODO: rename to `bedslope` + "bw": "BtmWdth", # TODO: rename to `bottomwidth` + "waterbody": "NHDWaterbodyComID", + "tw": "TopWdth", # TODO: rename to `topwidth` + "twcc": "TopWdthCC", # TODO: rename to `topwidthcc` + "musk": "MusK", + "musx": "MusX", + "cs": "ChSlp", # TODO: rename to `sideslope` + }, + "waterbody_null_code": -9999, + "terminal_code": 0, + "driver_string": "NetCDF", + "layer_string": 0, + } + + # specity output parameters + output_parameters["csv_output"] = None + output_parameters["nc_output_folder"] = None + + # specify restart parameters + restart_parameters["wrf_hydro_channel_restart_file"] = wrf_hydro_restart_file + restart_parameters["wrf_hydro_channel_ID_crosswalk_file"] = routelink_file + restart_parameters[ + "wrf_hydro_channel_ID_crosswalk_file_field_name" + ] = "link" + restart_parameters[ + "wrf_hydro_channel_restart_upstream_flow_field_name" + ] = "qlink1" + restart_parameters[ + "wrf_hydro_channel_restart_downstream_flow_field_name" + ] = "qlink2" + restart_parameters[ + "wrf_hydro_channel_restart_depth_flow_field_name" + ] = "hlink" + + # specify restart parameters + forcing_parameters["qlat_input_folder"] = os.path.join( + root, + "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", + ) + forcing_parameters["qlat_file_pattern_filter"] = "/*.CHRTOUT_DOMAIN1" + forcing_parameters["qlat_file_index_col"] = "feature_id" + forcing_parameters["qlat_file_value_col"] = "q_lateral" + + # construct time domain (run_parameters) + qlat_files = glob.glob( + forcing_parameters["qlat_input_folder"] + forcing_parameters["qlat_file_pattern_filter"], + recursive=True + ) + ql = nhd_io.get_ql_from_wrf_hydro( + qlat_files, + forcing_parameters["qlat_file_index_col"] + ) + + wrf_time = ql.columns.astype("datetime64[ns]") + dt_wrf = (wrf_time[1] - wrf_time[0]) + sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] + + dt = 300 + dt_routing = pd.Timedelta(str(dt) + 'seconds') + + run_parameters["dt"] = dt + run_parameters["nts"] = round(sim_duration / dt_routing) + run_parameters["qts_subdivisions"] = dt_wrf/dt_routing + + run_parameters["assume_short_ts"] = True + + # construct parity parameter set + validation_data = nhd_io.get_ql_from_wrf_hydro( + qlat_files, + forcing_parameters["qlat_file_index_col"], + "streamflow" + ) + + compare_node = 4186169 + parity_parameters["wrf_time"] = wrf_time + parity_parameters["dt_wrf"] = dt_wrf + parity_parameters["compare_node"] = compare_node + parity_parameters["validation_data"] = validation_data + + return supernetwork_parameters, run_parameters, output_parameters, restart_parameters, forcing_parameters, parity_parameters + +def parity_check(wrf_time, dt_wrf, nts, dt, validation_data, results, compare_node): + + # conpstruct a dataframe of simulated flows + fdv_columns = pd.MultiIndex.from_product([range(nts), ["q", "v", "d"]]) + flowveldepth = pd.concat( + [pd.DataFrame(d, index=i, columns=fdv_columns) for i, d in results], copy=False + ) + flowveldepth = flowveldepth.sort_index() + + flows = flowveldepth.loc[:, (slice(None), "q")] + flows = flows.T.reset_index(level = [0,1]) + flows.rename(columns = {"level_0": "Timestep", "level_1": "Parameter"}, inplace = True) + flows['Time (d)'] = ((flows.Timestep + 1) * dt)/(24*60*60) + flows = flows.set_index('Time (d)') + + # specify simulation calendar time + dt_routing = pd.Timedelta(str(dt) + 'seconds') + time_routing = pd.period_range((wrf_time[0]-dt_wrf+dt_routing), periods=nts, freq=dt_routing).astype("datetime64[ns]") + time_wrf = validation_data.columns.values + + # construct comparable dataframes + trt = pd.DataFrame(flows.loc[:,compare_node ].values, index = time_routing, columns = ["flow, t-route (cms)"]) + wrf = pd.DataFrame(validation_data.loc[compare_node ,:].values, index = time_wrf, columns = ["flow, wrf (cms)"]) + + # compare dataframes + compare = pd.concat([wrf, trt], axis=1, sort=False, join = 'inner') + print(compare) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index cf6dea9c3..f3a524dfd 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -76,20 +76,15 @@ def _handle_args(): default=144, type=int, ) + + # change this so after --test, the user enters a test choice parser.add_argument( "--test", - "--run-pocono2-test-example", - help="Use the data values stored in the repository for a test of the Pocono network", - dest="run_pocono2_test", - action="store_true", - ) - parser.add_argument( - "--test-full-pocono", - "--run-pocono1-test-example", - help="Use the data values stored in the repository for a test of the Mainstems_CONUS network", - dest="run_pocono1_test", - action="store_true", + help = "Select a test case, routing results will be compared against WRF hydro for parity", + choices=["pocono1"], + dest = "test_case" ) + parser.add_argument( "--sts", "--assume-short-ts", @@ -272,6 +267,7 @@ def _handle_args(): import mc_reach import troute.nhd_network as nhd_network import troute.nhd_io as nhd_io +import build_tests def writetoFile(file, writeString): @@ -355,7 +351,7 @@ def compute_nhd_routing_v02( def _input_handler(): args = _handle_args() - + custom_input_file = args.custom_input_file supernetwork_parameters = None waterbody_parameters = {} @@ -363,6 +359,7 @@ def _input_handler(): restart_parameters = {} output_parameters = {} run_parameters = {} + parity_parameters = {} if custom_input_file: ( @@ -372,6 +369,7 @@ def _input_handler(): restart_parameters, output_parameters, run_parameters, + parity_parameters ) = nhd_io.read_custom_input(custom_input_file) run_parameters["debuglevel"] *= -1 @@ -380,133 +378,34 @@ def _input_handler(): run_parameters["parallel_compute_method"] = args.parallel_compute_method run_parameters["cpu_pool"] = args.cpu_pool run_parameters["showtiming"] = args.showtiming - + run_parameters["debuglevel"] = debuglevel = -1 * args.debuglevel run_parameters["verbose"] = verbose = args.verbose test_folder = pathlib.Path(root, "test") geo_input_folder = test_folder.joinpath("input", "geo") - - run_pocono2_test = args.run_pocono2_test - run_pocono1_test = args.run_pocono1_test - - if run_pocono2_test: - if verbose: - print("running test case for Pocono_TEST2 domain") - # Overwrite the following test defaults - supernetwork = "Pocono_TEST2" - waterbody_parameters["break_network_at_waterbodies"] = False - run_parameters["qts_subdivisions"] = qts_subdivisions = 1 - run_parameters["dt"] = 300 / qts_subdivisions - run_parameters["nts"] = 144 * qts_subdivisions - output_parameters["csv_output"] = { - "csv_output_folder": os.path.join(root, "test", "output", "text") - } - output_parameters["nc_output_folder"] = os.path.join( - root, "test", "output", "text" - ) - # test 1. Take lateral flow from re-formatted wrf-hydro output from Pocono Basin simulation - forcing_parameters["qlat_input_file"] = os.path.join( - root, r"test/input/geo/PoconoSampleData2/Pocono_ql_testsamp1_nwm_mc.csv" - ) - - elif run_pocono1_test: - # NOTE: The test case for the Pocono basin was derived from this - # resource on HydroShare, developed by aaraney and sourced from the - # wrf_hydro_nwm_public repository on GitHub - # see: https://www.hydroshare.org/resource/03ca354200e540018d44183598890448/ - # By downloading aaraney's docker job scheduler repo from GitHub, one can - # execute the WRF-Hydro model that generated the test results - # see: https://github.com/aaraney/NWM-Dockerized-Job-Scheduler - if verbose: - print("running test case for Pocono_TEST1 domain") - # Overwrite the following test defaults - - NWM_test_path = os.path.join( - root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/" - ) - # lakeparm_file = os.path.join( - # NWM_test_path, "primary_domain", "DOMAIN", "LAKEPARM.nc", - # ) - routelink_file = os.path.join( - NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", - ) - time_string = "2017-12-31_06-00_DOMAIN1" - wrf_hydro_restart_file = os.path.join( - NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string - ) - supernetwork_parameters = { - "title_string": "Custom Input Example (using Pocono Test Example datafile)", - "geo_file_path": routelink_file, - "columns": { - "key": "link", - "downstream": "to", - "dx": "Length", - "n": "n", # TODO: rename to `manningn` - "ncc": "nCC", # TODO: rename to `mannningncc` - "s0": "So", # TODO: rename to `bedslope` - "bw": "BtmWdth", # TODO: rename to `bottomwidth` - "waterbody": "NHDWaterbodyComID", - "tw": "TopWdth", # TODO: rename to `topwidth` - "twcc": "TopWdthCC", # TODO: rename to `topwidthcc` - "musk": "MusK", - "musx": "MusX", - "cs": "ChSlp", # TODO: rename to `sideslope` - }, - "waterbody_null_code": -9999, - "terminal_code": 0, - "driver_string": "NetCDF", - "layer_string": 0, - } - # waterbody_parameters = { - # "level_pool": { - # "level_pool_waterbody_parameter_file_path": lakeparm_file, - # "level_pool_waterbody_id": "lake_id", - # "level_pool_waterbody_area": "LkArea", - # "level_pool_weir_elevation": "WeirE", - # "level_pool_waterbody_max_elevation": "LkMxE", - # "level_pool_outfall_weir_coefficient": "WeirC", - # "level_pool_outfall_weir_length": "WeirL", - # "level_pool_overall_dam_length": "DamL", - # "level_pool_orifice_elevation": "OrificeE", - # "level_pool_orifice_coefficient": "OrificeC", - # "level_pool_orifice_area": "OrificeA", - # } - # } - # break_network_at_waterbodies = True - run_parameters["qts_subdivisions"] = qts_subdivisions = 12 - run_parameters["dt"] = 3600 / qts_subdivisions - run_parameters["nts"] = 24 * qts_subdivisions - output_parameters["csv_output"] = None - output_parameters["nc_output_folder"] = None - # build a time string to specify input date - restart_parameters["wrf_hydro_channel_restart_file"] = wrf_hydro_restart_file - restart_parameters["wrf_hydro_channel_ID_crosswalk_file"] = routelink_file - restart_parameters[ - "wrf_hydro_channel_ID_crosswalk_file_field_name" - ] = "link" - restart_parameters[ - "wrf_hydro_channel_restart_upstream_flow_field_name" - ] = "qlink1" - restart_parameters[ - "wrf_hydro_channel_restart_downstream_flow_field_name" - ] = "qlink2" - restart_parameters[ - "wrf_hydro_channel_restart_depth_flow_field_name" - ] = "hlink" - # restart_parameters["wrf_hydro_waterbody_restart_file"] = wrf_hydro_restart_file - # restart_parameters["wrf_hydro_waterbody_ID_crosswalk_file"] = lakeparm_file - # restart_parameters["wrf_hydro_waterbody_ID_crosswalk_file_field_name"] = "lake_id" - # restart_parameters["wrf_hydro_waterbody_crosswalk_filter_file"] = routelink_file - # restart_parameters["wrf_hydro_waterbody_crosswalk_filter_file_field_name"] = "NHDWaterbodyComID" - # restart_parameters["wrf_hydro_waterbody_crosswalk_file_output_order_field= "AscendingIndex" - forcing_parameters["qlat_input_folder"] = os.path.join( - root, - "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", - ) - forcing_parameters["qlat_file_pattern_filter"] = "/*.CHRTOUT_DOMAIN1" - forcing_parameters["qlat_file_index_col"] = "feature_id" - forcing_parameters["qlat_file_value_col"] = "q_lateral" + + test_case = args.test_case + + if test_case: + + # call test case assemble function + ( + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters, + ) = build_tests.build_test_parameters( + test_case, + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters + ) else: run_parameters["dt"] = args.dt @@ -565,9 +464,9 @@ def _input_handler(): restart_parameters, output_parameters, run_parameters, + parity_parameters, ) - def main(): ( @@ -577,6 +476,7 @@ def main(): restart_parameters, output_parameters, run_parameters, + parity_parameters, ) = _input_handler() dt = run_parameters.get("dt", None) @@ -687,7 +587,23 @@ def main(): print("ordered reach computation complete") if showtiming: print("... in %s seconds." % (time.time() - start_time)) - + + + if "validation_data" in parity_parameters: + + if verbose: + print("conducting parity check, comparing t-route results against WRF Hydro results") + + build_tests.parity_check( + parity_parameters["wrf_time"], + parity_parameters["dt_wrf"], + run_parameters["nts"], + run_parameters["dt"], + parity_parameters["validation_data"], + results, + parity_parameters["compare_node"] + ) + if __name__ == "__main__": main() From 1a5ef5a224e24ac9c21098ef7797ca7b1ccc7734 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 18 Dec 2020 21:31:40 +0000 Subject: [PATCH 41/54] change tail/headwaters from yield to return --- src/python_framework_v02/troute/nhd_network.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_network.py b/src/python_framework_v02/troute/nhd_network.py index 5702141d5..de3004b48 100644 --- a/src/python_framework_v02/troute/nhd_network.py +++ b/src/python_framework_v02/troute/nhd_network.py @@ -93,12 +93,15 @@ def junctions(N): def headwaters(N): - yield from N.keys() - chain.from_iterable(N.values()) + return N.keys() - chain.from_iterable(N.values()) def tailwaters(N): - yield from chain.from_iterable(N.values()) - N.keys() - yield from (m for m, n in N.items() if not n) + tw = chain.from_iterable(N.values()) - N.keys() + for m, n in N.items(): + if not n: + tw.add(m) + return tw def reachable(N, sources=None, targets=None): From b0d9fc11621463ebd33d022371d8a8f15951c7cd Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 18 Dec 2020 21:32:58 +0000 Subject: [PATCH 42/54] fix Bug in disjoint check for unbranched networks --- src/python_framework_v02/troute/nhd_network.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/python_framework_v02/troute/nhd_network.py b/src/python_framework_v02/troute/nhd_network.py index de3004b48..43e5cacad 100644 --- a/src/python_framework_v02/troute/nhd_network.py +++ b/src/python_framework_v02/troute/nhd_network.py @@ -153,7 +153,11 @@ def reachable_network(N, sources=None, targets=None, check_disjoint=True): """ reached = reachable(N, sources=sources, targets=targets) - if check_disjoint and reduce(set.intersection, reached.values()): + if ( + check_disjoint + and len(reached) > 1 + and reduce(set.intersection, reached.values()) + ): raise ValueError("Networks not disjoint") rv = {} From 918cc75f5fed1e11f26c12474701873d0510a8d2 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 18 Dec 2020 21:35:47 +0000 Subject: [PATCH 43/54] Add Build Subnetworks Function --- .../troute/nhd_network.py | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/python_framework_v02/troute/nhd_network.py b/src/python_framework_v02/troute/nhd_network.py index 43e5cacad..d933969c6 100644 --- a/src/python_framework_v02/troute/nhd_network.py +++ b/src/python_framework_v02/troute/nhd_network.py @@ -426,3 +426,86 @@ def replace_waterbodies_connections(connections, waterbodies): # copy to new network unchanged new_conn[n] = connections[n] return new_conn + + +def build_subnetworks(connections, rconn, min_size, sources=None): + """ + Construct subnetworks using a truncated breadth-first-search + + Arguments: + connections + rconn + max_depth + sources + Returns: + subnetwork_master + """ + # if no sources provided, use tailwaters + if sources is None: + # identify tailwaters + sources = headwaters(rconn) + + # create a list of all headwaters in the network + all_hws = headwaters(connections) + + subnetwork_master = {} + for net in sources: + + # subnetwork creation using a breadth first search restricted by maximum allowable depth + #new_sources_list = [net] + new_sources = set([net]) + subnetworks = {} + group_order = 0 + while new_sources: + + # Build dict object containing reachable nodes within max_depth from each source in new_sources + rv = {} + for h in new_sources: + + reachable = set() + Q = deque([(h, 0)]) + stop_depth = 1000000 + while Q: + + x, y = Q.popleft() + reachable.add(x) + + rx = rconn.get(x, ()) + if len(rx) > 1: + us_depth = y + 1 + else: + us_depth = y + + if len(reachable) > min_size: + stop_depth = y + + if us_depth <= stop_depth: + Q.extend(zip(rx, [us_depth] * len(rx))) + + # reachable: a list of reachable segments within max_depth from source node h + rv[h] = reachable + + # find headwater segments in reachable groups, these will become the next set of sources + #new_sources_list = [] + new_sources = set() + for tw, seg in rv.items(): + # identify downstream connections for segments in this subnetwork + c = {key: connections[key] for key in seg} + # find apparent headwaters, will include new sources and actual headwaters + sub_hws = headwaters(c) + # extract new sources by differencing with list of actual headwaters + srcs = sub_hws - all_hws + # append list of new sources + new_sources.update(srcs) + # remove new sources from the subnetwork list + rv[tw].difference_update(srcs) + + # append master dictionary + subnetworks[group_order] = rv + + # advance group order + group_order += 1 + + subnetwork_master[net] = subnetworks + + return subnetwork_master From 802c58a719e6405f0241b4411e929ff64f781412 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 18 Dec 2020 21:44:18 +0000 Subject: [PATCH 44/54] add TODOs --- .../compute_nhd_routing_SingleSeg_v02.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index f3a524dfd..b1e683a6e 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -199,6 +199,7 @@ def _handle_args(): help="Name of the column providing the depth of flow at the beginning of the simulation.", default="hlink", ) + # TODO: Refine exclusivity of ql args (currently not going to accept more than one arg; more than one is needed for qlw, for instance.) ql_arg_group = parser.add_mutually_exclusive_group() ql_arg_group.add_argument( "--qlc", @@ -241,14 +242,6 @@ def _handle_args(): default="q_lateral", ) parser.add_argument("--ql", help="QLat input data", dest="ql", default=None) - # TODO: uncomment custominput file - # supernetwork_arg_group = parser.add_mutually_exclusive_group() - # supernetwork_arg_group.add_argument( - # "-f", - # "--custom-input-file", - # dest="custom_input_file", - # help="OR... please enter the path of a .yaml or .json file containing a custom supernetwork information. See for example test/input/yaml/CustomInput.yaml and test/input/json/CustomInput.json.", - # ) return parser.parse_args() @@ -267,7 +260,7 @@ def _handle_args(): import mc_reach import troute.nhd_network as nhd_network import troute.nhd_io as nhd_io -import build_tests +import build_tests # TODO: Determine whether and how to incorporate this into setup.py def writetoFile(file, writeString): From 9d42d843bad54733e533eee14c0c8acd5fe1dce9 Mon Sep 17 00:00:00 2001 From: James Halgren Date: Fri, 18 Dec 2020 21:48:26 +0000 Subject: [PATCH 45/54] add by-subnetwork parallel computation capability --- .../compute_nhd_routing_SingleSeg_v02.py | 305 +++++++++++++++++- .../fast_reach/mc_reach.pyx | 55 +++- 2 files changed, 349 insertions(+), 11 deletions(-) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index b1e683a6e..9b06d61de 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -21,6 +21,7 @@ import pathlib import glob import pandas as pd +from collections import defaultdict from functools import partial from joblib import delayed, Parallel from itertools import chain, islice @@ -121,12 +122,19 @@ def _handle_args(): dest="parallel_compute_method", const="by-network", ) + parser.add_argument( + "--subnet-size", + help="Set the target size (number of segments) for grouped subnetworks.", + dest="subnetwork_target_size", + default=-1, + type=int, + ) parser.add_argument( "--cpu-pool", help="Assign the number of cores to multiprocess across.", dest="cpu_pool", type=int, - default=None, + default=-1, ) parser.add_argument( "--compute-method", @@ -275,9 +283,12 @@ def constant_qlats(index_dataset, nsteps, qlat): def compute_nhd_routing_v02( + connections, + rconn, reaches_bytw, compute_func, parallel_compute_method, + subnetwork_target_size, cpu_pool, nts, qts_subdivisions, @@ -288,16 +299,283 @@ def compute_nhd_routing_v02( assume_short_ts, ): - if parallel_compute_method == "by-network": + start_time = time.time() + if parallel_compute_method == "by-subnetwork-jit-clustered": + networks_with_subnetworks_ordered_jit = nhd_network.build_subnetworks( + connections, rconn, subnetwork_target_size + ) + subnetworks_only_ordered_jit = defaultdict(dict) + subnetworks = defaultdict(dict) + for tw, ordered_network in networks_with_subnetworks_ordered_jit.items(): + intw = independent_networks[tw] + for order, subnet_sets in ordered_network.items(): + subnetworks_only_ordered_jit[order].update(subnet_sets) + for subn_tw, subnetwork in subnet_sets.items(): + subnetworks[subn_tw] = {k: intw[k] for k in subnetwork} + + reaches_ordered_bysubntw = defaultdict(dict) + for order, ordered_subn_dict in subnetworks_only_ordered_jit.items(): + for subn_tw, subnet in ordered_subn_dict.items(): + conn_subn = {k: connections[k] for k in subnet if k in connections} + rconn_subn = {k: rconn[k] for k in subnet if k in rconn} + path_func = partial(nhd_network.split_at_junction, rconn_subn) + reaches_ordered_bysubntw[order][ + subn_tw + ] = nhd_network.dfs_decomposition(rconn_subn, path_func) + + cluster_threshold = 0.65 # When a job has a total segment count 65% of the target size, compute it + # Otherwise, keep adding reaches. + + reaches_ordered_bysubntw_clustered = defaultdict(dict) + + for order in subnetworks_only_ordered_jit: + cluster = 0 + reaches_ordered_bysubntw_clustered[order][cluster] = { + "segs": [], + "upstreams": {}, + "tw": [], + "subn_reach_list": [], + } + for twi, (subn_tw, subn_reach_list) in enumerate( + reaches_ordered_bysubntw[order].items(), 1 + ): + segs = list(chain.from_iterable(subn_reach_list)) + reaches_ordered_bysubntw_clustered[order][cluster]["segs"].extend(segs) + reaches_ordered_bysubntw_clustered[order][cluster]["upstreams"].update( + subnetworks[subn_tw] + ) + + reaches_ordered_bysubntw_clustered[order][cluster]["tw"].append(subn_tw) + reaches_ordered_bysubntw_clustered[order][cluster][ + "subn_reach_list" + ].extend(subn_reach_list) + + if ( + len(reaches_ordered_bysubntw_clustered[order][cluster]["segs"]) + >= cluster_threshold * subnetwork_target_size + ) and ( + twi + < len(reaches_ordered_bysubntw[order]) + # i.e., we haven't reached the end + # TODO: perhaps this should be a while condition... + ): + cluster += 1 + reaches_ordered_bysubntw_clustered[order][cluster] = { + "segs": [], + "upstreams": {}, + "tw": [], + "subn_reach_list": [], + } + + if 1==1: + print("JIT Preprocessing time %s seconds." % (time.time() - start_time)) + print("starting Parallel JIT calculation") + + start_para_time = time.time() + # if 1 == 1: + with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: + results_subn = defaultdict(list) + flowveldepth_interorder = {} + + for order in range(max(subnetworks_only_ordered_jit.keys()), -1, -1): + jobs = [] + for cluster, clustered_subns in reaches_ordered_bysubntw_clustered[ + order + ].items(): + segs = clustered_subns["segs"] + offnetwork_upstreams = set() + segs_set = set(segs) + for seg in segs: + for us in rconn[seg]: + if us not in segs_set: + offnetwork_upstreams.add(us) + + segs.extend(offnetwork_upstreams) + param_df_sub = param_df.loc[ + segs, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + ].sort_index() + if order < max(subnetworks_only_ordered_jit.keys()): + for us_subn_tw in offnetwork_upstreams: + subn_tw_sortposition = param_df_sub.index.get_loc( + us_subn_tw + ) + flowveldepth_interorder[us_subn_tw][ + "position_index" + ] = subn_tw_sortposition + qlat_sub = qlats.loc[segs].sort_index() + q0_sub = q0.loc[segs].sort_index() + subn_reach_list = clustered_subns["subn_reach_list"] + upstreams = clustered_subns["upstreams"] + + # results_subn[order].append( + # compute_func( + jobs.append( + delayed(compute_func)( + nts, + qts_subdivisions, + subn_reach_list, + upstreams, + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, + qlat_sub.values, + q0_sub.values, + # flowveldepth_interorder, # obtain keys and values from this dataset + { + us: fvd + for us, fvd in flowveldepth_interorder.items() + if us in offnetwork_upstreams + }, + assume_short_ts, + ) + ) + results_subn[order] = parallel(jobs) + + if order > 0: # This is not needed for the last rank of subnetworks + flowveldepth_interorder = {} + for ci, (cluster, clustered_subns) in enumerate( + reaches_ordered_bysubntw_clustered[order].items() + ): + for subn_tw in clustered_subns["tw"]: + # TODO: This index step is necessary because we sort the segment index + # TODO: I think there are a number of ways we could remove the sorting step + # -- the binary search could be replaced with an index based on the known topology + flowveldepth_interorder[subn_tw] = {} + subn_tw_sortposition = ( + results_subn[order][ci][0].tolist().index(subn_tw) + ) + flowveldepth_interorder[subn_tw]["results"] = results_subn[ + order + ][ci][1][subn_tw_sortposition] + # what will it take to get just the tw FVD values into an array to pass to the next loop? + # There will be an empty array initialized at the top of the loop, then re-populated here. + # we don't have to bother with populating it after the last group + + results = [] + for order in subnetworks_only_ordered_jit: + results.extend(results_subn[order]) + + if 1==1: + print("PARALLEL TIME %s seconds." % (time.time() - start_para_time)) + + elif parallel_compute_method == "by-subnetwork-jit": + networks_with_subnetworks_ordered_jit = nhd_network.build_subnetworks( + connections, rconn, subnetwork_target_size + ) + subnetworks_only_ordered_jit = defaultdict(dict) + subnetworks = defaultdict(dict) + for tw, ordered_network in networks_with_subnetworks_ordered_jit.items(): + intw = independent_networks[tw] + for order, subnet_sets in ordered_network.items(): + subnetworks_only_ordered_jit[order].update(subnet_sets) + for subn_tw, subnetwork in subnet_sets.items(): + subnetworks[subn_tw] = {k: intw[k] for k in subnetwork} + + reaches_ordered_bysubntw = defaultdict(dict) + for order, ordered_subn_dict in subnetworks_only_ordered_jit.items(): + for subn_tw, subnet in ordered_subn_dict.items(): + conn_subn = {k: connections[k] for k in subnet if k in connections} + rconn_subn = {k: rconn[k] for k in subnet if k in rconn} + path_func = partial(nhd_network.split_at_junction, rconn_subn) + reaches_ordered_bysubntw[order][ + subn_tw + ] = nhd_network.dfs_decomposition(rconn_subn, path_func) + + if 1==1: + print("JIT Preprocessing time %s seconds." % (time.time() - start_time)) + print("starting Parallel JIT calculation") + + start_para_time = time.time() + with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: + results_subn = defaultdict(list) + flowveldepth_interorder = {} + + for order in range(max(subnetworks_only_ordered_jit.keys()), -1, -1): + jobs = [] + for twi, (subn_tw, subn_reach_list) in enumerate( + reaches_ordered_bysubntw[order].items(), 1 + ): + # TODO: Confirm that a list here is best -- we are sorting, + # so a set might be sufficient/better + segs = list(chain.from_iterable(subn_reach_list)) + offnetwork_upstreams = set() + segs_set = set(segs) + for seg in segs: + for us in rconn[seg]: + if us not in segs_set: + offnetwork_upstreams.add(us) + + segs.extend(offnetwork_upstreams) + param_df_sub = param_df.loc[ + segs, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + ].sort_index() + if order < max(subnetworks_only_ordered_jit.keys()): + for us_subn_tw in offnetwork_upstreams: + subn_tw_sortposition = param_df_sub.index.get_loc( + us_subn_tw + ) + flowveldepth_interorder[us_subn_tw][ + "position_index" + ] = subn_tw_sortposition + qlat_sub = qlats.loc[segs].sort_index() + q0_sub = q0.loc[segs].sort_index() + jobs.append( + delayed(compute_func)( + nts, + qts_subdivisions, + subn_reach_list, + subnetworks[subn_tw], + param_df_sub.index.values, + param_df_sub.columns.values, + param_df_sub.values, + qlat_sub.values, + q0_sub.values, + # flowveldepth_interorder, # obtain keys and values from this dataset + { + us: fvd + for us, fvd in flowveldepth_interorder.items() + if us in offnetwork_upstreams + }, + assume_short_ts, + ) + ) + + results_subn[order] = parallel(jobs) + + if order > 0: # This is not needed for the last rank of subnetworks + flowveldepth_interorder = {} + for twi, subn_tw in enumerate(reaches_ordered_bysubntw[order]): + # TODO: This index step is necessary because we sort the segment index + # TODO: I think there are a number of ways we could remove the sorting step + # -- the binary search could be replaced with an index based on the known topology + flowveldepth_interorder[subn_tw] = {} + subn_tw_sortposition = ( + results_subn[order][twi][0].tolist().index(subn_tw) + ) + flowveldepth_interorder[subn_tw]["results"] = results_subn[ + order + ][twi][1][subn_tw_sortposition] + # what will it take to get just the tw FVD values into an array to pass to the next loop? + # There will be an empty array initialized at the top of the loop, then re-populated here. + # we don't have to bother with populating it after the last group + + results = [] + for order in subnetworks_only_ordered_jit: + results.extend(results_subn[order]) + + if 1==1: + print("PARALLEL TIME %s seconds." % (time.time() - start_para_time)) + + elif parallel_compute_method == "by-network": with Parallel(n_jobs=cpu_pool, backend="threading") as parallel: jobs = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): - r = list(chain.from_iterable(reach_list)) + segs = list(chain.from_iterable(reach_list)) param_df_sub = param_df.loc[ - r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + segs, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() - qlat_sub = qlats.loc[r].sort_index() - q0_sub = q0.loc[r].sort_index() + qlat_sub = qlats.loc[segs].sort_index() + q0_sub = q0.loc[segs].sort_index() jobs.append( delayed(compute_func)( nts, @@ -309,6 +587,7 @@ def compute_nhd_routing_v02( param_df_sub.values, qlat_sub.values, q0_sub.values, + {}, assume_short_ts, ) ) @@ -317,12 +596,12 @@ def compute_nhd_routing_v02( else: # Execute in serial results = [] for twi, (tw, reach_list) in enumerate(reaches_bytw.items(), 1): - r = list(chain.from_iterable(reach_list)) + segs = list(chain.from_iterable(reach_list)) param_df_sub = param_df.loc[ - r, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] + segs, ["dt", "bw", "tw", "twcc", "dx", "n", "ncc", "cs", "s0"] ].sort_index() - qlat_sub = qlats.loc[r].sort_index() - q0_sub = q0.loc[r].sort_index() + qlat_sub = qlats.loc[segs].sort_index() + q0_sub = q0.loc[segs].sort_index() results.append( compute_func( nts, @@ -334,6 +613,7 @@ def compute_nhd_routing_v02( param_df_sub.values, qlat_sub.values, q0_sub.values, + {}, assume_short_ts, ) ) @@ -369,6 +649,7 @@ def _input_handler(): else: run_parameters["assume_short_ts"] = args.assume_short_ts run_parameters["parallel_compute_method"] = args.parallel_compute_method + run_parameters["subnetwork_target_size "] = args.subnetwork_target_size run_parameters["cpu_pool"] = args.cpu_pool run_parameters["showtiming"] = args.showtiming @@ -545,9 +826,13 @@ def main(): compute_func = mc_reach.compute_network results = compute_nhd_routing_v02( + connections, + rconn, reaches_bytw, compute_func, run_parameters.get("parallel_compute_method", None), + run_parameters.get("subnetwork_target_size", 1), + # The default here might be the whole network or some percentage... run_parameters.get("cpu_pool", None), run_parameters.get("nts", 1), run_parameters.get("qts_subdivisions", 1), diff --git a/src/python_routing_v02/fast_reach/mc_reach.pyx b/src/python_routing_v02/fast_reach/mc_reach.pyx index d1a7cdef4..3d08875ea 100644 --- a/src/python_routing_v02/fast_reach/mc_reach.pyx +++ b/src/python_routing_v02/fast_reach/mc_reach.pyx @@ -153,6 +153,7 @@ cpdef object compute_network( # const float[:] wbody_idx, # object[:] wbody_cols, # const float[:, :] wbody_vals, + dict upstream_results={}, bint assume_short_ts=False, ): """ @@ -168,6 +169,8 @@ cpdef object compute_network( assume_short_ts (bool): Assume short time steps (quc = qup) Notes: Array dimensions are checked as a precondition to this method. + data_idx inc. flowveldepth -- sorted numerically + Reach_buffer -- sorted topologically """ # Check shapes if qlat_values.shape[0] != data_idx.shape[0]: @@ -182,6 +185,37 @@ cpdef object compute_network( # rows: indexed by data_idx cdef float[:,::1] flowveldepth = np.zeros((data_idx.shape[0], nsteps * 3), dtype='float32') + # Pseudocode: LOOP ON Upstream Inflowers + # to pre-fill FlowVelDepth + # fill_index = list_of_all_segments_sorted -- .i.e, data_idx -- .index(upstream_tw_id) + # # FlowVelDepth[fill_index]['flow'] = UpstreamOutflows[upstream_tw_id]['flow'] + # # FlowVelDepth[fill_index]['depth'] = UpstreamOutflows[upstream_tw_id]['depth'] + + # for ts in flowveldepth: + # print(f"{list(ts)}") + + cdef set fill_index_mask = set() + # fill_index_mask is filled in the explicit loop below, which is + # identical to the following comprehension. But we use the explicit loop, because it + # is more transparent (and therefore optimizable) to cython. + # cdef set fill_index_mask = set([upstream_results[upstream_tw_id]["position_index"] for upstream_tw_id in upstream_results]) + #print(f"{fill_index_mask}") + + for upstream_tw_id in upstream_results: + fill_index = upstream_results[upstream_tw_id]["position_index"] + fill_index_mask.add(upstream_results[upstream_tw_id]["position_index"]) + # print(f"{upstream_results[upstream_tw_id]['results']}") + # print(f"filling the {fill_index} row:") + # print(f"{list(flowveldepth[fill_index])}") + for idx, val in enumerate(upstream_results[upstream_tw_id]["results"]): + flowveldepth[fill_index][idx] = val + # TODO: Identify a more efficient ways potentially to handle this array filling + # The following may be options: + # flowveldepth[fill_index] = upstream_results[upstream_tw_id]["results"] + # flowveldepth[fill_index, :] = upstream_results[upstream_tw_id]["results"] + # print(f"Now filled, it contains:") + # print(f"{list(flowveldepth[fill_index])}") + cdef: Py_ssize_t[:] srows # Source rows indexes Py_ssize_t[:] drows_tmp @@ -259,6 +293,16 @@ cpdef object compute_network( cdef int timestep = 0 cdef int ts_offset +# TODO: Split the compute network function so that the part where we set up +# all the indices is separate from the call to loop through them. +# That way, we can refine the functions for preparing the indexes in isolation +# For example, the actual looping function could start about here in the current +# function, and might look like the following Psuedocode +# cpdef(Dataindex): + # pull indices and put them in arrays + # minimal validation, + # Jump straight to nogil. + with nogil: while timestep < nsteps: ts_offset = timestep * 3 @@ -348,7 +392,16 @@ cpdef object compute_network( timestep += 1 - return np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32') + + # delete the duplicate results that shouldn't be passed along + # The upstream keys have empty results because they are not part of any reaches + # so we need to delete the null values that return + if len(fill_index_mask) > 0: + data_idx_ma = [ix for i, ix in enumerate(data_idx) if i not in fill_index_mask] + flowveldepth_ma = [ix for i, ix in enumerate(flowveldepth) if i not in fill_index_mask] + return [np.asarray(data_idx_ma, dtype=np.intp), np.asarray(flowveldepth_ma, dtype='float32')] + else: + return [np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32')] #---------------------------------------------------------------------------------------------------------------# #---------------------------------------------------------------------------------------------------------------# From 7869e916faaa5b218741bf45486b76d6163c9bf8 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Fri, 18 Dec 2020 15:33:32 -0700 Subject: [PATCH 46/54] move to --inplace setup.py --- .../mc_pylink_v00/MC_singleSeg_singleTS/makefile | 2 +- src/python_framework_v02/setup.py | 8 +++++--- src/python_routing_v02/compiler.sh | 11 +++++++++-- src/python_routing_v02/setup.py | 10 ++++++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/fortran_routing/mc_pylink_v00/MC_singleSeg_singleTS/makefile b/src/fortran_routing/mc_pylink_v00/MC_singleSeg_singleTS/makefile index a16a77b67..11245f95b 100644 --- a/src/fortran_routing/mc_pylink_v00/MC_singleSeg_singleTS/makefile +++ b/src/fortran_routing/mc_pylink_v00/MC_singleSeg_singleTS/makefile @@ -3,7 +3,7 @@ FC := gfortran # compile flags #FCFLAGS = -c -fdefault-real-8 -fno-align-commons -fbounds-check --free-form -FCFLAGS = -c -O2 -fPIC +FCFLAGS = -g -c -O2 -fPIC # link flags FLFLAGS = -static-gfortran -static-libgcc -no-defaultlibs -lgfortran -lgcc VPATH = ../Reservoir_singleTS diff --git a/src/python_framework_v02/setup.py b/src/python_framework_v02/setup.py index 388d30385..d7bfcfba3 100644 --- a/src/python_framework_v02/setup.py +++ b/src/python_framework_v02/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup +from setuptools import setup from distutils.extension import Extension import sys import numpy as np @@ -19,7 +19,9 @@ reach = Extension("troute.network.reach", sources = ["troute/network/reach.{}".format(ext)], include_dirs=[np.get_include()], - extra_objects = []) + extra_objects = [], + extra_compile_args = ['-g'], + ) package_data = { 'troute.network':['reach.pxd'] @@ -30,7 +32,7 @@ if USE_CYTHON: from Cython.Build import cythonize - ext_modules = cythonize(ext_modules) + ext_modules = cythonize(ext_modules, compiler_directives={"language_level": 3}) setup( name = 'Network', diff --git a/src/python_routing_v02/compiler.sh b/src/python_routing_v02/compiler.sh index b8e88c693..d5724c910 100755 --- a/src/python_routing_v02/compiler.sh +++ b/src/python_routing_v02/compiler.sh @@ -13,9 +13,16 @@ make install #creates troute package cd $REPOROOT/src/python_framework_v02 rm -rf build -python setup.py --use-cython install +#python setup.py --use-cython install +#python setup.py --use-cython develop +python setup.py build_ext --inplace --use-cython +pip install -e . #updates troute package with the execution script cd $REPOROOT/src/python_routing_v02 rm -rf build -python setup.py --use-cython install +#python setup.py --use-cython install +#python setup.py --use-cython develop +python setup.py build_ext --inplace --use-cython +pip install -e . + diff --git a/src/python_routing_v02/setup.py b/src/python_routing_v02/setup.py index 666828379..cef38c7dc 100644 --- a/src/python_routing_v02/setup.py +++ b/src/python_routing_v02/setup.py @@ -1,4 +1,4 @@ -from distutils.core import setup +from setuptools import setup from distutils.extension import Extension import sys import numpy as np @@ -19,14 +19,16 @@ reach = Extension("reach", sources = ["fast_reach/reach.{}".format(ext)], - extra_objects = ['fast_reach/mc_single_seg.o', 'fast_reach/pymc_single_seg.o' ]) + extra_objects = ['fast_reach/mc_single_seg.o', 'fast_reach/pymc_single_seg.o' ], + extra_compile_args=['-g']) mc_reach = Extension("mc_reach", sources = ["fast_reach/mc_reach.{}".format(ext)], include_dirs = [np.get_include()], libraries=[], library_dirs=[], - extra_objects=[]) + extra_objects=[], + extra_compile_args=['-g']) ext_modules=[ reach, mc_reach @@ -34,7 +36,7 @@ if USE_CYTHON: from Cython.Build import cythonize - ext_modules = cythonize(ext_modules) + ext_modules = cythonize(ext_modules, compiler_directives={'language_level': 3}) setup( name = 'compute_network_mc', From f6036ebc1477d087490690b76d4dc81a78ed8ba7 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Tue, 5 Jan 2021 17:01:06 -0700 Subject: [PATCH 47/54] use xarray.open_mfdataset() to load qlat --- src/python_framework_v02/troute/nhd_io.py | 58 +++++++++++++++++++ .../troute/nhd_network_utilities_v02.py | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index 338f50415..b982cd400 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -136,6 +136,64 @@ def read_qlat(path): return get_ql_from_csv(path) +def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_lateral"): + """ + qlat_files: globbed list of CHRTOUT files containing desired lateral inflows + index_col: column/field in the CHRTOUT files with the segment/link id + value_col: column/field in the CHRTOUT files with the lateral inflow value + + In general the CHRTOUT files contain one value per time step. At present, there is + no capability for handling non-uniform timesteps in the qlaterals. + + The qlateral may also be input using comma delimited file -- see + `get_ql_from_csv` + + + Note: + For later needs, filtering may be accomplished with one of: + ds.loc[{selectors}] + ds.sel({selectors}) + ds.isel({selectors}) + + Returns from these selection functions are sub-datasets. + + For example: + ``` + (Pdb) ds.sel({"feature_id":[4186117],"time":ds.time.values[:2]})['q_lateral'].to_dataframe() + latitude longitude q_lateral + time feature_id + 2018-01-01 13:00:00 4186117 41.233807 -75.413895 0.006496 + 2018-01-02 00:00:00 4186117 41.233807 -75.413895 0.006460 + ``` + + or... + ``` + (Pdb) ds.sel({"feature_id":[4186117],"time":[np.datetime64('2018-01-01T13:00:00')]})['q_lateral'].to_dataframe() + latitude longitude q_lateral + time feature_id + 2018-01-01 13:00:00 4186117 41.233807 -75.413895 0.006496 + ``` + """ + + # TODO: compare performance with `combine='by_coords'` + # NOTE: This check for len(qlat_files) is because of a potential bug in + # in open_mfdataset which strips the unit time dimension from an + # input where only one timestamp (i.e., one input file) is provided. + # Assuming the bug is resolved at some point, the 'by_coords' + # method should be better for all cases. + if len(qlat_files) == 1: + with xr.open_mfdataset(qlat_files, combine='nested', concat_dim='time') as ds: + df1 = ds[value_col].to_dataframe() + else: # Assumes > 1 + with xr.open_mfdataset(qlat_files, combine='by_coords') as ds: + df1 = ds[value_col].to_dataframe() + + mod = df1.reset_index() + ql = mod.pivot(index=index_col, columns="time", values=value_col) + + return ql + + def get_ql_from_wrf_hydro(qlat_files, index_col="station_id", value_col="q_lateral"): """ qlat_files: globbed list of CHRTOUT files containing desired lateral inflows diff --git a/src/python_framework_v02/troute/nhd_network_utilities_v02.py b/src/python_framework_v02/troute/nhd_network_utilities_v02.py index 4078a1751..9d611d667 100644 --- a/src/python_framework_v02/troute/nhd_network_utilities_v02.py +++ b/src/python_framework_v02/troute/nhd_network_utilities_v02.py @@ -484,7 +484,7 @@ def build_qlateral_array(forcing_parameters, connections_keys, nts): ) qlat_file_value_col = forcing_parameters.get("qlat_file_value_col", "q_lateral") qlat_files = glob.glob(qlat_input_folder + qlat_file_pattern_filter) - qlat_df = nhd_io.get_ql_from_wrf_hydro( + qlat_df = nhd_io.get_ql_from_wrf_hydro_mf( qlat_files=qlat_files, index_col=qlat_file_index_col, value_col=qlat_file_value_col, From 8c25635528f01113b89e67de0982da83277cc257 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Wed, 6 Jan 2021 11:50:46 -0700 Subject: [PATCH 48/54] move processing out of parity_parameters parity_parameters now contains only parameters -- the actual parity values are computed in later steps. --- src/python_framework_v02/troute/nhd_io.py | 4 +- src/python_routing_v02/build_tests.py | 74 +++++++++++-------- .../compute_nhd_routing_SingleSeg_v02.py | 7 +- 3 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index b982cd400..9f4e68205 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -53,6 +53,7 @@ def read_custom_input(custom_input_file): restart_parameters = data.get("restart_parameters", {}) output_parameters = data.get("output_parameters", {}) run_parameters = data.get("run_parameters", {}) + parity_parameters = data.get("parity_parameters", {}) # TODO: add error trapping for potentially missing files return ( supernetwork_parameters, @@ -61,6 +62,7 @@ def read_custom_input(custom_input_file): restart_parameters, output_parameters, run_parameters, + parity_parameters, ) @@ -136,7 +138,7 @@ def read_qlat(path): return get_ql_from_csv(path) -def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_lateral"): +def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_lateral", filter_list=None): """ qlat_files: globbed list of CHRTOUT files containing desired lateral inflows index_col: column/field in the CHRTOUT files with the segment/link id diff --git a/src/python_routing_v02/build_tests.py b/src/python_routing_v02/build_tests.py index 3749d304f..0971074fd 100644 --- a/src/python_routing_v02/build_tests.py +++ b/src/python_routing_v02/build_tests.py @@ -117,46 +117,60 @@ def build_test_parameters(test_name, forcing_parameters["qlat_file_value_col"] = "q_lateral" # construct time domain (run_parameters) - qlat_files = glob.glob( - forcing_parameters["qlat_input_folder"] + forcing_parameters["qlat_file_pattern_filter"], - recursive=True - ) - ql = nhd_io.get_ql_from_wrf_hydro( - qlat_files, - forcing_parameters["qlat_file_index_col"] - ) + #qlat_files = glob.glob( + #forcing_parameters["qlat_input_folder"] + forcing_parameters["qlat_file_pattern_filter"], + #recursive=True + #) + #ql = nhd_io.get_ql_from_wrf_hydro_mf( + #qlat_files, + #forcing_parameters["qlat_file_index_col"] + #) - wrf_time = ql.columns.astype("datetime64[ns]") - dt_wrf = (wrf_time[1] - wrf_time[0]) - sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] + #wrf_time = ql.columns.astype("datetime64[ns]") + #dt_wrf = (wrf_time[1] - wrf_time[0]) + #sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] - dt = 300 - dt_routing = pd.Timedelta(str(dt) + 'seconds') + #dt = 300 + #dt_routing = pd.Timedelta(str(dt) + 'seconds') - run_parameters["dt"] = dt - run_parameters["nts"] = round(sim_duration / dt_routing) - run_parameters["qts_subdivisions"] = dt_wrf/dt_routing + run_parameters["dt"] = 300 + run_parameters["nts"] = 288 + run_parameters["qts_subdivisions"] = 12 run_parameters["assume_short_ts"] = True - # construct parity parameter set - validation_data = nhd_io.get_ql_from_wrf_hydro( - qlat_files, - forcing_parameters["qlat_file_index_col"], - "streamflow" + parity_parameters["parity_check_input_folder"] = os.path.join( + root, + "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", ) - - compare_node = 4186169 - parity_parameters["wrf_time"] = wrf_time - parity_parameters["dt_wrf"] = dt_wrf - parity_parameters["compare_node"] = compare_node - parity_parameters["validation_data"] = validation_data + parity_parameters["parity_check_file_pattern_filter"] = "/*.CHRTOUT_DOMAIN1" + parity_parameters["parity_check_file_index_col"] = "feature_id" + parity_parameters["parity_check_file_value_col"] = "streamflow" + parity_parameters["parity_check_compare_node"] = 4186169 return supernetwork_parameters, run_parameters, output_parameters, restart_parameters, forcing_parameters, parity_parameters -def parity_check(wrf_time, dt_wrf, nts, dt, validation_data, results, compare_node): - - # conpstruct a dataframe of simulated flows +def parity_check(parity_parameters, nts, dt, results): + validation_files = glob.glob( + parity_parameters["parity_check_input_folder"] + parity_parameters["parity_check_file_pattern_filter"], + recursive=True + ) + + compare_node = parity_parameters["parity_check_compare_node"] + + # construct parity parameter set + validation_data = nhd_io.get_ql_from_wrf_hydro_mf( + validation_files, + parity_parameters["parity_check_file_index_col"], + parity_parameters["parity_check_file_value_col"], + [compare_node], + ) + + wrf_time = validation_data.columns.astype("datetime64[ns]") + dt_wrf = (wrf_time[1] - wrf_time[0]) + sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] + + # construct a dataframe of simulated flows fdv_columns = pd.MultiIndex.from_product([range(nts), ["q", "v", "d"]]) flowveldepth = pd.concat( [pd.DataFrame(d, index=i, columns=fdv_columns) for i, d in results], copy=False diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 9b06d61de..954d53563 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -867,19 +867,16 @@ def main(): print("... in %s seconds." % (time.time() - start_time)) - if "validation_data" in parity_parameters: + if "parity_check_input_folder" in parity_parameters: if verbose: print("conducting parity check, comparing t-route results against WRF Hydro results") build_tests.parity_check( - parity_parameters["wrf_time"], - parity_parameters["dt_wrf"], + parity_parameters, run_parameters["nts"], run_parameters["dt"], - parity_parameters["validation_data"], results, - parity_parameters["compare_node"] ) From 2d250d7e43ced2dd503acb147fb83d5155bb56d1 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Wed, 6 Jan 2021 11:52:11 -0700 Subject: [PATCH 49/54] allow filter --- src/python_framework_v02/troute/nhd_io.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index 9f4e68205..7c97c017a 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -185,10 +185,17 @@ def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_la # method should be better for all cases. if len(qlat_files) == 1: with xr.open_mfdataset(qlat_files, combine='nested', concat_dim='time') as ds: - df1 = ds[value_col].to_dataframe() + if not filter_list: + df1 = ds[value_col].to_dataframe() + else: + df1 = ds.sel({index_col:filter_list})[value_col].to_dataframe() + else: # Assumes > 1 with xr.open_mfdataset(qlat_files, combine='by_coords') as ds: - df1 = ds[value_col].to_dataframe() + if not filter_list: + df1 = ds[value_col].to_dataframe() + else: + df1 = ds.sel({index_col:filter_list})[value_col].to_dataframe() mod = df1.reset_index() ql = mod.pivot(index=index_col, columns="time", values=value_col) @@ -213,7 +220,7 @@ def get_ql_from_wrf_hydro(qlat_files, index_col="station_id", value_col="q_later for filename in qlat_files: with xr.open_dataset(filename) as ds: - df1 = ds.to_dataframe() + df1 = ds[value_col].to_dataframe() li.append(df1) From 04da1b6b062d03a89493530875f9c8707fdc928c Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Wed, 6 Jan 2021 11:56:08 -0700 Subject: [PATCH 50/54] black --- src/python_framework_v02/troute/nhd_io.py | 20 +-- .../troute/nhd_network.py | 4 +- src/python_routing_v02/build_tests.py | 137 ++++++++++-------- .../compute_nhd_routing_SingleSeg_v02.py | 75 +++++----- src/python_routing_v02/setup.py | 42 +++--- 5 files changed, 149 insertions(+), 129 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index 7c97c017a..fae4649e8 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -79,7 +79,7 @@ def replace_downstreams(data, downstream_col, terminal_code): def read_waterbody_df(waterbody_parameters, waterbodies_values, wbtype="level_pool"): """ General waterbody dataframe reader. At present, only level-pool - capability exists. + capability exists. """ if wbtype == "level_pool": wb_params = waterbody_parameters[wbtype] @@ -94,14 +94,14 @@ def read_level_pool_waterbody_df( parm_file, lake_index_field="lake_id", lake_id_mask=None ): """ - Reads LAKEPARM file and prepares a dataframe, filtered + Reads LAKEPARM file and prepares a dataframe, filtered to the relevant reservoirs, to provide the parameters for level-pool reservoir computation. Completely replaces the read_waterbody_df function from prior versions of the v02 routing code. - Prior version filtered the dataframe as opposed to the dataset as in this version. + Prior version filtered the dataframe as opposed to the dataset as in this version. with xr.open_dataset(parm_file) as ds: df1 = ds.to_dataframe() df1 = df1.set_index(lake_index_field).sort_index(axis="index") @@ -138,7 +138,9 @@ def read_qlat(path): return get_ql_from_csv(path) -def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_lateral", filter_list=None): +def get_ql_from_wrf_hydro_mf( + qlat_files, index_col="station_id", value_col="q_lateral", filter_list=None +): """ qlat_files: globbed list of CHRTOUT files containing desired lateral inflows index_col: column/field in the CHRTOUT files with the segment/link id @@ -184,18 +186,18 @@ def get_ql_from_wrf_hydro_mf(qlat_files, index_col="station_id", value_col="q_la # Assuming the bug is resolved at some point, the 'by_coords' # method should be better for all cases. if len(qlat_files) == 1: - with xr.open_mfdataset(qlat_files, combine='nested', concat_dim='time') as ds: + with xr.open_mfdataset(qlat_files, combine="nested", concat_dim="time") as ds: if not filter_list: df1 = ds[value_col].to_dataframe() else: - df1 = ds.sel({index_col:filter_list})[value_col].to_dataframe() + df1 = ds.sel({index_col: filter_list})[value_col].to_dataframe() - else: # Assumes > 1 - with xr.open_mfdataset(qlat_files, combine='by_coords') as ds: + else: # Assumes > 1 + with xr.open_mfdataset(qlat_files, combine="by_coords") as ds: if not filter_list: df1 = ds[value_col].to_dataframe() else: - df1 = ds.sel({index_col:filter_list})[value_col].to_dataframe() + df1 = ds.sel({index_col: filter_list})[value_col].to_dataframe() mod = df1.reset_index() ql = mod.pivot(index=index_col, columns="time", values=value_col) diff --git a/src/python_framework_v02/troute/nhd_network.py b/src/python_framework_v02/troute/nhd_network.py index d933969c6..ee24eb842 100644 --- a/src/python_framework_v02/troute/nhd_network.py +++ b/src/python_framework_v02/troute/nhd_network.py @@ -452,7 +452,7 @@ def build_subnetworks(connections, rconn, min_size, sources=None): for net in sources: # subnetwork creation using a breadth first search restricted by maximum allowable depth - #new_sources_list = [net] + # new_sources_list = [net] new_sources = set([net]) subnetworks = {} group_order = 0 @@ -486,7 +486,7 @@ def build_subnetworks(connections, rconn, min_size, sources=None): rv[h] = reachable # find headwater segments in reachable groups, these will become the next set of sources - #new_sources_list = [] + # new_sources_list = [] new_sources = set() for tw, seg in rv.items(): # identify downstream connections for segments in this subnetwork diff --git a/src/python_routing_v02/build_tests.py b/src/python_routing_v02/build_tests.py index 0971074fd..5dab4edcb 100644 --- a/src/python_routing_v02/build_tests.py +++ b/src/python_routing_v02/build_tests.py @@ -33,17 +33,19 @@ elif not ENV_IS_CL: root = pathlib.Path("../..").resolve() -def build_test_parameters(test_name, - supernetwork_parameters, - run_parameters, - output_parameters, - restart_parameters, - forcing_parameters, - parity_parameters - ): - + +def build_test_parameters( + test_name, + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters, +): + if test_name == "pocono1": - + print("running test case for Pocono_TEST1 domain - NO RESERVOIRS") # File path to WRF Hydro data @@ -55,13 +57,13 @@ def build_test_parameters(test_name, routelink_file = os.path.join( NWM_test_path, "primary_domain", "DOMAIN", "Route_Link.nc", ) - + # Speficify WRF hydro restart file, name and destination time_string = "2017-12-31_06-00_DOMAIN1" wrf_hydro_restart_file = os.path.join( NWM_test_path, "example_RESTART", "HYDRO_RST." + time_string ) - + # specify supernetwork parameters supernetwork_parameters = { "title_string": "Custom Input Example (using Pocono Test Example datafile)", @@ -86,26 +88,22 @@ def build_test_parameters(test_name, "driver_string": "NetCDF", "layer_string": 0, } - + # specity output parameters output_parameters["csv_output"] = None output_parameters["nc_output_folder"] = None - + # specify restart parameters restart_parameters["wrf_hydro_channel_restart_file"] = wrf_hydro_restart_file restart_parameters["wrf_hydro_channel_ID_crosswalk_file"] = routelink_file - restart_parameters[ - "wrf_hydro_channel_ID_crosswalk_file_field_name" - ] = "link" + restart_parameters["wrf_hydro_channel_ID_crosswalk_file_field_name"] = "link" restart_parameters[ "wrf_hydro_channel_restart_upstream_flow_field_name" ] = "qlink1" restart_parameters[ "wrf_hydro_channel_restart_downstream_flow_field_name" ] = "qlink2" - restart_parameters[ - "wrf_hydro_channel_restart_depth_flow_field_name" - ] = "hlink" + restart_parameters["wrf_hydro_channel_restart_depth_flow_field_name"] = "hlink" # specify restart parameters forcing_parameters["qlat_input_folder"] = os.path.join( @@ -115,30 +113,30 @@ def build_test_parameters(test_name, forcing_parameters["qlat_file_pattern_filter"] = "/*.CHRTOUT_DOMAIN1" forcing_parameters["qlat_file_index_col"] = "feature_id" forcing_parameters["qlat_file_value_col"] = "q_lateral" - + # construct time domain (run_parameters) - #qlat_files = glob.glob( - #forcing_parameters["qlat_input_folder"] + forcing_parameters["qlat_file_pattern_filter"], - #recursive=True - #) - #ql = nhd_io.get_ql_from_wrf_hydro_mf( - #qlat_files, - #forcing_parameters["qlat_file_index_col"] - #) - - #wrf_time = ql.columns.astype("datetime64[ns]") - #dt_wrf = (wrf_time[1] - wrf_time[0]) - #sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] - - #dt = 300 - #dt_routing = pd.Timedelta(str(dt) + 'seconds') - + # qlat_files = glob.glob( + # forcing_parameters["qlat_input_folder"] + forcing_parameters["qlat_file_pattern_filter"], + # recursive=True + # ) + # ql = nhd_io.get_ql_from_wrf_hydro_mf( + # qlat_files, + # forcing_parameters["qlat_file_index_col"] + # ) + + # wrf_time = ql.columns.astype("datetime64[ns]") + # dt_wrf = (wrf_time[1] - wrf_time[0]) + # sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] + + # dt = 300 + # dt_routing = pd.Timedelta(str(dt) + 'seconds') + run_parameters["dt"] = 300 run_parameters["nts"] = 288 - run_parameters["qts_subdivisions"] = 12 - + run_parameters["qts_subdivisions"] = 12 + run_parameters["assume_short_ts"] = True - + parity_parameters["parity_check_input_folder"] = os.path.join( root, "test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", @@ -147,13 +145,22 @@ def build_test_parameters(test_name, parity_parameters["parity_check_file_index_col"] = "feature_id" parity_parameters["parity_check_file_value_col"] = "streamflow" parity_parameters["parity_check_compare_node"] = 4186169 - - return supernetwork_parameters, run_parameters, output_parameters, restart_parameters, forcing_parameters, parity_parameters + + return ( + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters, + ) + def parity_check(parity_parameters, nts, dt, results): validation_files = glob.glob( - parity_parameters["parity_check_input_folder"] + parity_parameters["parity_check_file_pattern_filter"], - recursive=True + parity_parameters["parity_check_input_folder"] + + parity_parameters["parity_check_file_pattern_filter"], + recursive=True, ) compare_node = parity_parameters["parity_check_compare_node"] @@ -167,31 +174,41 @@ def parity_check(parity_parameters, nts, dt, results): ) wrf_time = validation_data.columns.astype("datetime64[ns]") - dt_wrf = (wrf_time[1] - wrf_time[0]) + dt_wrf = wrf_time[1] - wrf_time[0] sim_duration = (wrf_time[-1] + dt_wrf) - wrf_time[0] - + # construct a dataframe of simulated flows fdv_columns = pd.MultiIndex.from_product([range(nts), ["q", "v", "d"]]) flowveldepth = pd.concat( [pd.DataFrame(d, index=i, columns=fdv_columns) for i, d in results], copy=False ) flowveldepth = flowveldepth.sort_index() - + flows = flowveldepth.loc[:, (slice(None), "q")] - flows = flows.T.reset_index(level = [0,1]) - flows.rename(columns = {"level_0": "Timestep", "level_1": "Parameter"}, inplace = True) - flows['Time (d)'] = ((flows.Timestep + 1) * dt)/(24*60*60) - flows = flows.set_index('Time (d)') - + flows = flows.T.reset_index(level=[0, 1]) + flows.rename(columns={"level_0": "Timestep", "level_1": "Parameter"}, inplace=True) + flows["Time (d)"] = ((flows.Timestep + 1) * dt) / (24 * 60 * 60) + flows = flows.set_index("Time (d)") + # specify simulation calendar time - dt_routing = pd.Timedelta(str(dt) + 'seconds') - time_routing = pd.period_range((wrf_time[0]-dt_wrf+dt_routing), periods=nts, freq=dt_routing).astype("datetime64[ns]") - time_wrf = validation_data.columns.values - + dt_routing = pd.Timedelta(str(dt) + "seconds") + time_routing = pd.period_range( + (wrf_time[0] - dt_wrf + dt_routing), periods=nts, freq=dt_routing + ).astype("datetime64[ns]") + time_wrf = validation_data.columns.values + # construct comparable dataframes - trt = pd.DataFrame(flows.loc[:,compare_node ].values, index = time_routing, columns = ["flow, t-route (cms)"]) - wrf = pd.DataFrame(validation_data.loc[compare_node ,:].values, index = time_wrf, columns = ["flow, wrf (cms)"]) - + trt = pd.DataFrame( + flows.loc[:, compare_node].values, + index=time_routing, + columns=["flow, t-route (cms)"], + ) + wrf = pd.DataFrame( + validation_data.loc[compare_node, :].values, + index=time_wrf, + columns=["flow, wrf (cms)"], + ) + # compare dataframes - compare = pd.concat([wrf, trt], axis=1, sort=False, join = 'inner') + compare = pd.concat([wrf, trt], axis=1, sort=False, join="inner") print(compare) diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 954d53563..80d3adfa2 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -77,15 +77,15 @@ def _handle_args(): default=144, type=int, ) - + # change this so after --test, the user enters a test choice parser.add_argument( "--test", - help = "Select a test case, routing results will be compared against WRF hydro for parity", + help="Select a test case, routing results will be compared against WRF hydro for parity", choices=["pocono1"], - dest = "test_case" + dest="test_case", ) - + parser.add_argument( "--sts", "--assume-short-ts", @@ -283,7 +283,7 @@ def constant_qlats(index_dataset, nsteps, qlat): def compute_nhd_routing_v02( - connections, + connections, rconn, reaches_bytw, compute_func, @@ -367,7 +367,7 @@ def compute_nhd_routing_v02( "subn_reach_list": [], } - if 1==1: + if 1 == 1: print("JIT Preprocessing time %s seconds." % (time.time() - start_time)) print("starting Parallel JIT calculation") @@ -455,7 +455,7 @@ def compute_nhd_routing_v02( for order in subnetworks_only_ordered_jit: results.extend(results_subn[order]) - if 1==1: + if 1 == 1: print("PARALLEL TIME %s seconds." % (time.time() - start_para_time)) elif parallel_compute_method == "by-subnetwork-jit": @@ -481,7 +481,7 @@ def compute_nhd_routing_v02( subn_tw ] = nhd_network.dfs_decomposition(rconn_subn, path_func) - if 1==1: + if 1 == 1: print("JIT Preprocessing time %s seconds." % (time.time() - start_time)) print("starting Parallel JIT calculation") @@ -563,7 +563,7 @@ def compute_nhd_routing_v02( for order in subnetworks_only_ordered_jit: results.extend(results_subn[order]) - if 1==1: + if 1 == 1: print("PARALLEL TIME %s seconds." % (time.time() - start_para_time)) elif parallel_compute_method == "by-network": @@ -624,7 +624,7 @@ def compute_nhd_routing_v02( def _input_handler(): args = _handle_args() - + custom_input_file = args.custom_input_file supernetwork_parameters = None waterbody_parameters = {} @@ -642,7 +642,7 @@ def _input_handler(): restart_parameters, output_parameters, run_parameters, - parity_parameters + parity_parameters, ) = nhd_io.read_custom_input(custom_input_file) run_parameters["debuglevel"] *= -1 @@ -652,34 +652,34 @@ def _input_handler(): run_parameters["subnetwork_target_size "] = args.subnetwork_target_size run_parameters["cpu_pool"] = args.cpu_pool run_parameters["showtiming"] = args.showtiming - + run_parameters["debuglevel"] = debuglevel = -1 * args.debuglevel run_parameters["verbose"] = verbose = args.verbose test_folder = pathlib.Path(root, "test") geo_input_folder = test_folder.joinpath("input", "geo") - + test_case = args.test_case - + if test_case: - + # call test case assemble function ( - supernetwork_parameters, - run_parameters, - output_parameters, - restart_parameters, + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, + forcing_parameters, + parity_parameters, + ) = build_tests.build_test_parameters( + test_case, + supernetwork_parameters, + run_parameters, + output_parameters, + restart_parameters, forcing_parameters, parity_parameters, - ) = build_tests.build_test_parameters( - test_case, - supernetwork_parameters, - run_parameters, - output_parameters, - restart_parameters, - forcing_parameters, - parity_parameters - ) + ) else: run_parameters["dt"] = args.dt @@ -741,6 +741,7 @@ def _input_handler(): parity_parameters, ) + def main(): ( @@ -865,20 +866,18 @@ def main(): print("ordered reach computation complete") if showtiming: print("... in %s seconds." % (time.time() - start_time)) - - + if "parity_check_input_folder" in parity_parameters: - + if verbose: - print("conducting parity check, comparing t-route results against WRF Hydro results") - + print( + "conducting parity check, comparing t-route results against WRF Hydro results" + ) + build_tests.parity_check( - parity_parameters, - run_parameters["nts"], - run_parameters["dt"], - results, + parity_parameters, run_parameters["nts"], run_parameters["dt"], results, ) - + if __name__ == "__main__": main() diff --git a/src/python_routing_v02/setup.py b/src/python_routing_v02/setup.py index cef38c7dc..3f6b5a541 100644 --- a/src/python_routing_v02/setup.py +++ b/src/python_routing_v02/setup.py @@ -9,36 +9,38 @@ and builds using distutils. """ -if '--use-cython' in sys.argv: +if "--use-cython" in sys.argv: USE_CYTHON = True - sys.argv.remove('--use-cython') + sys.argv.remove("--use-cython") else: USE_CYTHON = False -ext = 'pyx' if USE_CYTHON else 'c' +ext = "pyx" if USE_CYTHON else "c" -reach = Extension("reach", - sources = ["fast_reach/reach.{}".format(ext)], - extra_objects = ['fast_reach/mc_single_seg.o', 'fast_reach/pymc_single_seg.o' ], - extra_compile_args=['-g']) +reach = Extension( + "reach", + sources=["fast_reach/reach.{}".format(ext)], + extra_objects=["fast_reach/mc_single_seg.o", "fast_reach/pymc_single_seg.o"], + extra_compile_args=["-g"], +) -mc_reach = Extension("mc_reach", - sources = ["fast_reach/mc_reach.{}".format(ext)], - include_dirs = [np.get_include()], - libraries=[], - library_dirs=[], - extra_objects=[], - extra_compile_args=['-g']) +mc_reach = Extension( + "mc_reach", + sources=["fast_reach/mc_reach.{}".format(ext)], + include_dirs=[np.get_include()], + libraries=[], + library_dirs=[], + extra_objects=[], + extra_compile_args=["-g"], +) -ext_modules=[ - reach, mc_reach - ] +ext_modules = [reach, mc_reach] if USE_CYTHON: from Cython.Build import cythonize - ext_modules = cythonize(ext_modules, compiler_directives={'language_level': 3}) + + ext_modules = cythonize(ext_modules, compiler_directives={"language_level": 3}) setup( - name = 'compute_network_mc', - ext_modules = ext_modules, + name="compute_network_mc", ext_modules=ext_modules, ) From 87996bc0684257fb75d3504fe993ab0f567f7063 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Fri, 8 Jan 2021 21:06:30 -0700 Subject: [PATCH 51/54] update mf_dataset load to optimize for serial loading --- src/python_framework_v02/troute/nhd_io.py | 59 ++++++++++--------- src/python_routing_v02/build_tests.py | 2 +- .../compute_nhd_routing_SingleSeg_v02.py | 2 +- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/python_framework_v02/troute/nhd_io.py b/src/python_framework_v02/troute/nhd_io.py index fae4649e8..2f5ce2448 100644 --- a/src/python_framework_v02/troute/nhd_io.py +++ b/src/python_framework_v02/troute/nhd_io.py @@ -139,7 +139,7 @@ def read_qlat(path): def get_ql_from_wrf_hydro_mf( - qlat_files, index_col="station_id", value_col="q_lateral", filter_list=None + qlat_files, index_col="feature_id", value_col="q_lateral" ): """ qlat_files: globbed list of CHRTOUT files containing desired lateral inflows @@ -153,8 +153,9 @@ def get_ql_from_wrf_hydro_mf( `get_ql_from_csv` - Note: - For later needs, filtering may be accomplished with one of: + Note/Todo: + For later needs, filtering for specific features or times may + be accomplished with one of: ds.loc[{selectors}] ds.sel({selectors}) ds.isel({selectors}) @@ -163,7 +164,7 @@ def get_ql_from_wrf_hydro_mf( For example: ``` - (Pdb) ds.sel({"feature_id":[4186117],"time":ds.time.values[:2]})['q_lateral'].to_dataframe() + (Pdb) ds.sel({"feature_id":[4186117, 4186169],"time":ds.time.values[:2]})['q_lateral'].to_dataframe() latitude longitude q_lateral time feature_id 2018-01-01 13:00:00 4186117 41.233807 -75.413895 0.006496 @@ -172,39 +173,39 @@ def get_ql_from_wrf_hydro_mf( or... ``` - (Pdb) ds.sel({"feature_id":[4186117],"time":[np.datetime64('2018-01-01T13:00:00')]})['q_lateral'].to_dataframe() + (Pdb) ds.sel({"feature_id":[4186117, 4186169],"time":[np.datetime64('2018-01-01T13:00:00')]})['q_lateral'].to_dataframe() latitude longitude q_lateral time feature_id 2018-01-01 13:00:00 4186117 41.233807 -75.413895 0.006496 ``` """ - - # TODO: compare performance with `combine='by_coords'` - # NOTE: This check for len(qlat_files) is because of a potential bug in - # in open_mfdataset which strips the unit time dimension from an - # input where only one timestamp (i.e., one input file) is provided. - # Assuming the bug is resolved at some point, the 'by_coords' - # method should be better for all cases. - if len(qlat_files) == 1: - with xr.open_mfdataset(qlat_files, combine="nested", concat_dim="time") as ds: - if not filter_list: - df1 = ds[value_col].to_dataframe() - else: - df1 = ds.sel({index_col: filter_list})[value_col].to_dataframe() - - else: # Assumes > 1 - with xr.open_mfdataset(qlat_files, combine="by_coords") as ds: - if not filter_list: - df1 = ds[value_col].to_dataframe() - else: - df1 = ds.sel({index_col: filter_list})[value_col].to_dataframe() - - mod = df1.reset_index() - ql = mod.pivot(index=index_col, columns="time", values=value_col) + filter_list = None + + with xr.open_mfdataset( + qlat_files, + combine="by_coords", + # combine="nested", + # concat_dim="time", + # data_vars="minimal", + # coords="minimal", + # compat="override", + preprocess=drop_all_coords, + # parallel=True, + ) as ds: + ql = pd.DataFrame( + ds[value_col].values.T, + index=ds[index_col].values, + columns=ds.time.values, + # dtype=float, + ) return ql +def drop_all_coords(ds): + return ds.reset_coords(drop=True) + + def get_ql_from_wrf_hydro(qlat_files, index_col="station_id", value_col="q_lateral"): """ qlat_files: globbed list of CHRTOUT files containing desired lateral inflows @@ -222,7 +223,7 @@ def get_ql_from_wrf_hydro(qlat_files, index_col="station_id", value_col="q_later for filename in qlat_files: with xr.open_dataset(filename) as ds: - df1 = ds[value_col].to_dataframe() + df1 = ds[["time", value_col]].to_dataframe() li.append(df1) diff --git a/src/python_routing_v02/build_tests.py b/src/python_routing_v02/build_tests.py index 5dab4edcb..8eb1d7677 100644 --- a/src/python_routing_v02/build_tests.py +++ b/src/python_routing_v02/build_tests.py @@ -170,7 +170,7 @@ def parity_check(parity_parameters, nts, dt, results): validation_files, parity_parameters["parity_check_file_index_col"], parity_parameters["parity_check_file_value_col"], - [compare_node], + # [compare_node], ) wrf_time = validation_data.columns.astype("datetime64[ns]") diff --git a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py index 80d3adfa2..349df227b 100644 --- a/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py +++ b/src/python_routing_v02/compute_nhd_routing_SingleSeg_v02.py @@ -871,7 +871,7 @@ def main(): if verbose: print( - "conducting parity check, comparing t-route results against WRF Hydro results" + "conducting parity check, comparing WRF Hydro results against t-route results" ) build_tests.parity_check( From cd9f62c8f5284758a7de715d33c832f72d2d0283 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Mon, 11 Jan 2021 16:03:31 -0700 Subject: [PATCH 52/54] Add parity check parameters to json/yaml --- test/input/json/CustomInput.json | 8 ++++++++ test/input/yaml/CustomInput.yaml | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/test/input/json/CustomInput.json b/test/input/json/CustomInput.json index c95834bc2..407ada591 100644 --- a/test/input/json/CustomInput.json +++ b/test/input/json/CustomInput.json @@ -86,5 +86,13 @@ "wrf_hydro_waterbody_ID_crosswalk_file_field_name": "lake_id", "wrf_hydro_waterbody_crosswalk_filter_file": "../../test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/primary_domain/DOMAIN/Route_Link.nc", "wrf_hydro_waterbody_crosswalk_filter_file_field_name": "NHDWaterbodyComID" + }, + "parity_parameters": + { + "parity_check_input_folder": "../../test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/", + "parity_check_file_pattern_filter": "/*.CHRTOUT_DOMAIN1", + "parity_check_file_index_col": "feature_id", + "parity_check_file_value_col": "streamflow", + "parity_check_compare_node": 4186169 } } diff --git a/test/input/yaml/CustomInput.yaml b/test/input/yaml/CustomInput.yaml index 3e7ea88b4..23b4e126f 100644 --- a/test/input/yaml/CustomInput.yaml +++ b/test/input/yaml/CustomInput.yaml @@ -94,4 +94,10 @@ restart_parameters: #WRF-Hydro waterbody crosswalk filter file wrf_hydro_waterbody_crosswalk_filter_file: "../../test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/primary_domain/DOMAIN/Route_Link.nc" wrf_hydro_waterbody_crosswalk_filter_file_field_name: NHDWaterbodyComID +parity_parameters: + parity_check_input_folder: "../../test/input/geo/NWM_2.1_Sample_Datasets/Pocono_TEST1/example_CHRTOUT/" + parity_check_file_pattern_filter: "/*.CHRTOUT_DOMAIN1" + parity_check_file_index_col: feature_id + parity_check_file_value_col: streamflow + parity_check_compare_node: 4186169 From d0a0fe829324ce9873c60d09055226ffd8c8dcc4 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Wed, 13 Jan 2021 15:02:27 -0700 Subject: [PATCH 53/54] add comment placeholder Some removed functionality in the build_tests script may be re-incorporated. --- src/python_routing_v02/build_tests.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/python_routing_v02/build_tests.py b/src/python_routing_v02/build_tests.py index 8eb1d7677..8c02b0f58 100644 --- a/src/python_routing_v02/build_tests.py +++ b/src/python_routing_v02/build_tests.py @@ -131,6 +131,10 @@ def build_test_parameters( # dt = 300 # dt_routing = pd.Timedelta(str(dt) + 'seconds') + # run_parameters["dt"] = dt + # run_parameters["nts"] = round(sim_duration / dt_routing) + # run_parameters["qts_subdivisions"] = dt_wrf/dt_routing + run_parameters["dt"] = 300 run_parameters["nts"] = 288 run_parameters["qts_subdivisions"] = 12 From 7ddd2a3a1f616eb19f61d834c3f0e75af1dbb4a9 Mon Sep 17 00:00:00 2001 From: jameshalgren Date: Wed, 13 Jan 2021 15:28:31 -0700 Subject: [PATCH 54/54] remove square brackets from compute_network return --- src/python_routing_v02/fast_reach/mc_reach.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_routing_v02/fast_reach/mc_reach.pyx b/src/python_routing_v02/fast_reach/mc_reach.pyx index 3d08875ea..0b56fdc19 100644 --- a/src/python_routing_v02/fast_reach/mc_reach.pyx +++ b/src/python_routing_v02/fast_reach/mc_reach.pyx @@ -399,9 +399,9 @@ cpdef object compute_network( if len(fill_index_mask) > 0: data_idx_ma = [ix for i, ix in enumerate(data_idx) if i not in fill_index_mask] flowveldepth_ma = [ix for i, ix in enumerate(flowveldepth) if i not in fill_index_mask] - return [np.asarray(data_idx_ma, dtype=np.intp), np.asarray(flowveldepth_ma, dtype='float32')] + return np.asarray(data_idx_ma, dtype=np.intp), np.asarray(flowveldepth_ma, dtype='float32') else: - return [np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32')] + return np.asarray(data_idx, dtype=np.intp), np.asarray(flowveldepth, dtype='float32') #---------------------------------------------------------------------------------------------------------------# #---------------------------------------------------------------------------------------------------------------#