import datetime
import pathlib
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import earthaccess
import matplotlib.pyplot as plt
import netCDF4 as nc
import numpy as np
import pandas as pd
From the PO.DAAC Cookbook, to access the GitHub version of the notebook, follow this link.
Exploring river discharge in the SWORD of Science (SoS) dataset
Summary
The SWORD of Science (SoS) is a community-driven dataset produced for and from the execution of the Confluence workflow in the cloud which enables quick data access and compute on SWOT data. Data granules contain two files, priors and results. The priors file contains prior information, such as in-situ gage data and model output that is used to generate the discharge products. The results file contains the resulting river discharge data products.
The cloud-based workflow (“Confluence”) that produces the SoS will produce discharge parameter estimates which the SWOT mission will use to produce discharge. This discharge will be stored in the SWOT shapefiles as the official SWOT discharge. However, the Confluence workflow produces discharge time series alongside the discharge parameter estimates in order to preview what will eventually stored in the SWOT shapefiles. Users can reference the SoS for the latest discharge time series recognizing that the official SWOT discharge data product lives in the SWOT shapefiles.
The SoS is organized by continent following SWOT River Database (SWORD) structure and naming conventions. It is indexed on the same reach and node identifier dimensions found in SWORD. Time series data is stored by cycle and pass on an observation dimension.
More information is available in the SWOT-Confluence Github repository: * Documentation for priors * Documentation for results
Results are organized into groups corresponding to modules in the SWOT-Confluence processing software. Modules are described in the Confluence Module Documentation.
Table of Modules (Algorithms) and Discharge variables
The following lists the algorithms alongside their discharge variables and location in the SoS results file assuming that the SoS is an open file represented by the results
variable.
The first six reference discharge time series for the flow law parameter estimate (FLPE) algorithms while the last six reference the Mean Optimization Integrator (MOI) results. The MOI uses a river topology to force mass conservation at confluences to produce a discharge time series for each reach-level FLPE.
Module (Algorithm) | Discharge Variable | Location in the SoS |
---|---|---|
HiVDI | Q | results[“hivdi”][“Q”] |
MetroMan | allq | results[“metroman”][“allq”] |
MOMMA | Q | results[“momma”][“Q”] |
neoBAM | q1, q2, or q3 | results[“neobam”][“q”][“q1”] |
SAD | Qa | results[“sad”][“Qa”] |
SIC4DVar | Q_da | results[“sic4dvar”][“Q_da”] |
MOI HiVDI | q | results[“moi”][“hivdi”][“q”] |
MOI MetroMan | q | results[“moi”][“metroman”][“q”] |
MOI MOMMA | q | results[“moi”][“momma”][“q”] |
MOI neoBAM | q | results[“moi”][“qeobam”][“q”] |
MOI SAD | q | results[“moi”][“sad”][“q”] |
MOI SIC4DVar | q | results[“moi”][“sic4dvar”][“q”] |
Requirements
1. Compute environment
This tutorial can be run in the following environments: - Local compute environment e.g. laptop, server: this tutorial can be run on your local machine
2. Earthdata Login
An Earthdata Login account is required to access data, as well as discover restricted data, from the NASA Earthdata system. Thus, to access NASA data, you need Earthdata Login. Please visit https://urs.earthdata.nasa.gov to register and manage your Earthdata Login account. This account is free to create and only takes a moment to set up.
Learning Objectives
- To explore and begin to understand the structure of the SoS.
- To locate and plot river discharge.
Import Packages
Authenticate
Authenticate your Earthdata Login (EDL) information using the earthaccess
python package as follows:
# Login with your EDL credentials if asked earthaccess.login()
<earthaccess.auth.Auth at 0x16aa132c0>
Search and Access SoS data
Locate the SoS data of interest and then download for access.
# Search and locate granules
= earthaccess.search_data(
granule_info ="SWOT_L4_DAWG_SOS_DISCHARGE",
short_name=("2023-04-07", "2023-04-26"),
temporal
) granule_info
Granules found: 3
[Collection: {'Version': '1', 'ShortName': 'SWOT_L4_DAWG_SOS_DISCHARGE'}
Spatial coverage: {'HorizontalSpatialDomain': {'Geometry': {'BoundingRectangles': [{'WestBoundingCoordinate': -21.794, 'SouthBoundingCoordinate': 25.382, 'EastBoundingCoordinate': 25.382, 'NorthBoundingCoordinate': 81.115}]}}}
Temporal coverage: {'RangeDateTime': {'EndingDateTime': '2023-04-25T20:01:59.000Z', 'BeginningDateTime': '2023-04-07T22:49:35.000Z'}}
Size(MB): 983.0999364852905
Data: ['https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/eu_sword_v15_SOS_unconstrained_0001_20240228T205029_results.nc', 'https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/eu_sword_v15_SOS_unconstrained_0001_20240228T205029_priors.nc'],
Collection: {'Version': '1', 'ShortName': 'SWOT_L4_DAWG_SOS_DISCHARGE'}
Spatial coverage: {'HorizontalSpatialDomain': {'Geometry': {'BoundingRectangles': [{'WestBoundingCoordinate': -81.139, 'SouthBoundingCoordinate': -52, 'EastBoundingCoordinate': -52, 'NorthBoundingCoordinate': 11.097}]}}}
Temporal coverage: {'RangeDateTime': {'EndingDateTime': '2023-04-26T12:04:55.000Z', 'BeginningDateTime': '2023-04-08T01:51:07.000Z'}}
Size(MB): 1700.4334163665771
Data: ['https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/sa_sword_v15_SOS_unconstrained_0001_20240228T205034_results.nc', 'https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/sa_sword_v15_SOS_unconstrained_0001_20240228T205034_priors.nc'],
Collection: {'Version': '1', 'ShortName': 'SWOT_L4_DAWG_SOS_DISCHARGE'}
Spatial coverage: {'HorizontalSpatialDomain': {'Geometry': {'BoundingRectangles': [{'WestBoundingCoordinate': -166.397, 'SouthBoundingCoordinate': 8.09, 'EastBoundingCoordinate': 8.09, 'NorthBoundingCoordinate': 82.311}]}}}
Temporal coverage: {'RangeDateTime': {'EndingDateTime': '2023-04-26T13:28:35.000Z', 'BeginningDateTime': '2023-04-08T05:36:12.000Z'}}
Size(MB): 1613.2776679992676
Data: ['https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/na_sword_v15_SOS_unconstrained_0001_20240228T205032_results.nc', 'https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/na_sword_v15_SOS_unconstrained_0001_20240228T205032_priors.nc']]
# Enter a directory path to store downloaded data in
= pathlib.Path("data_downloads")
downloads_dir =True, exist_ok=True)
downloads_dir.mkdir(parents
# Select a priors and results pair to explore
= [[link for link in earthaccess.results.DataGranule.data_links(granule)] for granule in granule_info]
download_links print("Select a priors and results file to explore:")
for downloads in download_links:
for download in downloads:
if "priors" in download: print(download)
Select a priors and results file to explore:
https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/eu_sword_v15_SOS_unconstrained_0001_20240228T205029_priors.nc
https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/sa_sword_v15_SOS_unconstrained_0001_20240228T205034_priors.nc
https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/na_sword_v15_SOS_unconstrained_0001_20240228T205032_priors.nc
# Select Europe ("eu") priors file to work with
= "https://archive.podaac.earthdata.nasa.gov/podaac-ops-cumulus-protected/SWOT_L4_DAWG_SOS_DISCHARGE/eu_sword_v15_SOS_unconstrained_0001_20240228T205029_priors.nc"
priors_link
# Select results
= priors_link.replace("priors", "results")
results_link
earthaccess.download(priors_link, downloads_dir) earthaccess.download(results_link, downloads_dir)
File eu_sword_v15_SOS_unconstrained_0001_20240228T205029_priors.nc already downloaded
File eu_sword_v15_SOS_unconstrained_0001_20240228T205029_results.nc already downloaded
['data_downloads/eu_sword_v15_SOS_unconstrained_0001_20240228T205029_results.nc']
# Open downloaded files to access SoS granule data
= priors_link.split('/')[-1]
priors_download = results_link.split('/')[-1]
results_download
= nc.Dataset(downloads_dir.joinpath(priors_download), format="NETCDF4")
priors = nc.Dataset(downloads_dir.joinpath(results_download), format="NETCDF4") results
Explore the SoS
We can now explore the SoS using either the data read directly from S3 or downloaded to your local computer.
# Constants
# Select a river
= "Rhine"
RIVER_NAME
# Select a discharge algorithm (hivdi, neobam, metroman, momma, sad, sic4dvar)
= "hivdi"
DISCHARGE_ALGORITHM = "Q" DISCHARGE_VARIABLE
# Display the priors groups
print("Priors Groups:")
print(priors.groups)
Priors Groups:
{'reaches': <class 'netCDF4._netCDF4.Group'>
group /reaches:
dimensions(sizes):
variables(dimensions): int64 reach_id(num_reaches), float64 x(num_reaches), float64 y(num_reaches), <class 'str'> river_name(num_reaches)
groups: , 'nodes': <class 'netCDF4._netCDF4.Group'>
group /nodes:
dimensions(sizes):
variables(dimensions): int64 node_id(num_nodes), int64 reach_id(num_nodes), float64 x(num_nodes), float64 y(num_nodes), <class 'str'> river_name(num_nodes)
groups: , 'model': <class 'netCDF4._netCDF4.Group'>
group /model:
dimensions(sizes): num_months(12), probability(20)
variables(dimensions): int32 num_months(num_months), int32 probability(probability), float64 flow_duration_q(num_reaches, probability), float64 max_q(num_reaches), float64 monthly_q(num_reaches, num_months), float64 mean_q(num_reaches), float64 min_q(num_reaches), float64 two_year_return_q(num_reaches), int32 area_estimate_flag(num_reaches)
groups: , 'gbpriors': <class 'netCDF4._netCDF4.Group'>
group /gbpriors:
dimensions(sizes):
variables(dimensions):
groups: reach, node, 'EAU': <class 'netCDF4._netCDF4.Group'>
group /EAU:
dimensions(sizes): num_days(16130), num_months(12), probability(20), nchars(100), num_EAU_reaches(243)
variables(dimensions): int32 num_days(num_days), int32 CAL(num_EAU_reaches), int32 EAU_reaches(num_EAU_reaches), int64 EAU_reach_id(num_EAU_reaches), float64 EAU_flow_duration_q(num_EAU_reaches, probability), float64 EAU_max_q(num_EAU_reaches), float64 EAU_monthly_q(num_EAU_reaches, num_months), float64 EAU_mean_q(num_EAU_reaches), float64 EAU_min_q(num_EAU_reaches), float64 EAU_two_year_return_q(num_EAU_reaches), |S1 EAU_id(num_EAU_reaches, nchars), float64 EAU_q(num_EAU_reaches, num_days), float64 EAU_qt(num_EAU_reaches, num_days)
groups: , 'DEFRA': <class 'netCDF4._netCDF4.Group'>
group /DEFRA:
dimensions(sizes): num_days(16130), num_months(12), probability(20), nchars(100), num_DEFRA_reaches(26)
variables(dimensions): int32 num_days(num_days), int32 CAL(num_DEFRA_reaches), int32 DEFRA_reaches(num_DEFRA_reaches), int64 DEFRA_reach_id(num_DEFRA_reaches), float64 DEFRA_flow_duration_q(num_DEFRA_reaches, probability), float64 DEFRA_max_q(num_DEFRA_reaches), float64 DEFRA_monthly_q(num_DEFRA_reaches, num_months), float64 DEFRA_mean_q(num_DEFRA_reaches), float64 DEFRA_min_q(num_DEFRA_reaches), float64 DEFRA_two_year_return_q(num_DEFRA_reaches), |S1 DEFRA_id(num_DEFRA_reaches, nchars), float64 DEFRA_q(num_DEFRA_reaches, num_days), float64 DEFRA_qt(num_DEFRA_reaches, num_days)
groups: }
# Display the module groups
print("Results Groups:")
print(results.groups)
Results Groups:
{'reaches': <class 'netCDF4._netCDF4.Group'>
group /reaches:
dimensions(sizes):
variables(dimensions): int64 reach_id(num_reaches), float64 x(num_reaches), float64 y(num_reaches), <class 'str'> river_name(num_reaches), int32 observations(num_reaches), float64 time(num_reaches)
groups: , 'nodes': <class 'netCDF4._netCDF4.Group'>
group /nodes:
dimensions(sizes):
variables(dimensions): int64 node_id(num_nodes), int64 reach_id(num_nodes), float64 x(num_nodes), float64 y(num_nodes), <class 'str'> river_name(num_nodes), int32 observations(num_nodes), float64 time(num_nodes)
groups: , 'hivdi': <class 'netCDF4._netCDF4.Group'>
group /hivdi:
dimensions(sizes):
variables(dimensions): float64 Q(num_reaches), float64 A0(num_reaches), float64 beta(num_reaches), float64 alpha(num_reaches)
groups: , 'metroman': <class 'netCDF4._netCDF4.Group'>
group /metroman:
dimensions(sizes):
variables(dimensions): float64 allq(num_reaches), float64 A0hat(num_reaches), float64 nahat(num_reaches), float64 x1hat(num_reaches), float64 q_u(num_reaches)
groups: , 'moi': <class 'netCDF4._netCDF4.Group'>
group /moi:
dimensions(sizes):
variables(dimensions):
groups: geobam, hivdi, metroman, momma, sad, sic4dvar, 'momma': <class 'netCDF4._netCDF4.Group'>
group /momma:
dimensions(sizes):
variables(dimensions): float64 stage(num_reaches), float64 width(num_reaches), float64 slope(num_reaches), float64 Qgage(num_reaches), float64 seg(num_reaches), float64 n(num_reaches), float64 Y(num_reaches), float64 v(num_reaches), float64 Q(num_reaches), float64 Q_constrained(num_reaches), float64 gage_constrained(num_reaches), float64 input_Qm_prior(num_reaches), float64 input_Qb_prior(num_reaches), float64 input_Yb_prior(num_reaches), float64 input_known_ezf(num_reaches), float64 input_known_bkfl_stage(num_reaches), float64 input_known_nb_seg1(num_reaches), float64 input_known_x_seg1(num_reaches), float64 Qgage_constrained_nb_seg1(num_reaches), float64 Qgage_constrained_x_seg1(num_reaches), float64 input_known_nb_seg2(num_reaches), float64 input_known_x_seg2(num_reaches), float64 Qgage_constrained_nb_seg2(num_reaches), float64 Qgage_constrained_x_seg2(num_reaches), float64 n_bkfl_Qb_prior(num_reaches), float64 n_bkfl_slope(num_reaches), float64 vel_bkfl_Qb_prior(num_reaches), float64 Froude_bkfl_diag_Smean(num_reaches), float64 width_bkfl_solved_obs(num_reaches), float64 depth_bkfl_solved_obs(num_reaches), float64 depth_bkfl_diag_Wb_Smean(num_reaches), float64 zero_flow_stage(num_reaches), float64 bankfull_stage(num_reaches), float64 Qmean_prior(num_reaches), float64 Qmean_momma(num_reaches), float64 Qmean_momma.constrained(num_reaches), float64 width_stage_corr(num_reaches)
groups: , 'neobam': <class 'netCDF4._netCDF4.Group'>
group /neobam:
dimensions(sizes):
variables(dimensions):
groups: r, logn, logDb, logWb, q, 'offline': <class 'netCDF4._netCDF4.Group'>
group /offline:
dimensions(sizes):
variables(dimensions): float64 d_x_area(num_reaches), float64 d_x_area_u(num_reaches), float64 metro_q_c(num_reaches), float64 bam_q_c(num_reaches), float64 hivdi_q_c(num_reaches), float64 momma_q_c(num_reaches), float64 sads_q_c(num_reaches), float64 consensus_q_c(num_reaches), float64 metro_q_uc(num_reaches), float64 bam_q_uc(num_reaches), float64 hivdi_q_uc(num_reaches), float64 momma_q_uc(num_reaches), float64 sads_q_uc(num_reaches), float64 consensus_q_uc(num_reaches)
groups: , 'postdiagnostics': <class 'netCDF4._netCDF4.Group'>
group /postdiagnostics:
dimensions(sizes): nchar(10)
variables(dimensions):
groups: basin, reach, 'prediagnostics': <class 'netCDF4._netCDF4.Group'>
group /prediagnostics:
dimensions(sizes):
variables(dimensions):
groups: reach, node, 'sad': <class 'netCDF4._netCDF4.Group'>
group /sad:
dimensions(sizes):
variables(dimensions): float64 A0(num_reaches), float64 n(num_reaches), float64 Qa(num_reaches), float64 Q_u(num_reaches)
groups: , 'sic4dvar': <class 'netCDF4._netCDF4.Group'>
group /sic4dvar:
dimensions(sizes):
variables(dimensions): float64 A0(num_reaches), float64 n(num_reaches), float64 Q_mm(num_reaches), float64 Q_da(num_reaches)
groups: , 'validation': <class 'netCDF4._netCDF4.Group'>
group /validation:
dimensions(sizes): num_algos(14), nchar(16)
variables(dimensions): |S1 algo_names(num_reaches, num_algos, nchar), int32 has_validation(num_reaches), float64 nse(num_reaches, num_algos), float64 rsq(num_reaches, num_algos), float64 kge(num_reaches, num_algos), float64 rmse(num_reaches, num_algos), float64 testn(num_reaches, num_algos), float64 nrmse(num_reaches, num_algos), float64 nbias(num_reaches, num_algos), float64 rrmse(num_reaches, num_algos)
groups: }
Plot river reach locations
Information about the spatial location of river reaches is in the reaches and nodes groups including river names. This data is taken directly from SWOT River Database (SWORD).
= results.groups['reaches'] # Access the reaches group
reaches
print("Reaches Group")
print(reaches, "\n")
print("Longitude")
print(reaches.variables['x'])
Reaches Group
<class 'netCDF4._netCDF4.Group'>
group /reaches:
dimensions(sizes):
variables(dimensions): int64 reach_id(num_reaches), float64 x(num_reaches), float64 y(num_reaches), <class 'str'> river_name(num_reaches), int32 observations(num_reaches), float64 time(num_reaches)
groups:
Longitude
<class 'netCDF4._netCDF4.Variable'>
float64 x(num_reaches)
long_name: longitude
comment: longitude of the reach center decimal ranging from 180°E to 180°W
units: degrees_east
valid_min: -180.0
valid_max: 180.0
coverage_content_type: coordinate
path = /reaches
unlimited dimensions:
current shape = (30768,)
filling on, default _FillValue of 9.969209968386869e+36 used
# Unpack the spatial coordinates and river names
= results.groups['reaches'].variables['x']
reach_lon = results.groups['reaches'].variables['y']
reach_lat
= results.groups['reaches'].variables['river_name']
river_names
# Filter data to only find the river of interest
= np.where(river_names[:] == RIVER_NAME)
idx print(f"Indexes for {RIVER_NAME}:\n {idx}")
Indexes for Rhine:
(array([12597, 12598, 12599, 12600, 12601, 12602, 12603, 12606, 12616,
12617, 12618, 12619, 12620, 12621, 12622, 12623, 12624, 12625,
12626, 12627, 12628, 12629, 12630, 12631, 12634, 12635, 12636,
12638, 12639, 12640, 12769, 12918, 12919, 12920, 12923, 12924,
12925, 12926, 12931, 12932, 12933, 12938, 12939, 12940, 12941,
12942, 12943, 12944, 13098, 13099, 13100, 13101, 13102, 13152,
13153, 13154, 13155, 13156, 13157, 13158, 13159, 13160, 13161,
13162, 13163, 13164, 13165, 13166, 13168, 13169, 13170, 13172,
13173, 13174, 13175, 13176, 13177, 13178, 13179, 13180, 13181,
13182, 13183, 13184, 13185, 13186, 13187, 13188, 13189, 13190,
13191, 13192, 13193, 13194, 13195, 13196, 13197, 13198, 13199,
13200, 13201, 13202, 13204, 13205, 13206, 13207, 13208, 13316,
13317, 13319, 13324, 13325, 13326, 13327, 13328, 13329, 13330,
13331, 13332, 13333, 13334, 13335, 13336, 13337, 13339, 13340,
13341, 13342, 13343, 13345, 13346, 13362, 13363, 13364, 13365,
13366, 13367, 13368, 13369, 13370, 13371, 13372, 13373, 13374,
13375, 13385]),)
# Plot the location of the river
= plt.figure(figsize=(10,10))
fig
# Add map elements gridlines
= plt.axes(projection=ccrs.PlateCarree())
ax
ax.coastlines()='black')
ax.add_feature(cfeature.STATES, edgecolor
= ax.gridlines(crs=ccrs.PlateCarree(), linewidth=1, color='black', alpha=0.5, linestyle='--', draw_labels=True)
gl = False
gl.xlabels_top = True
gl.ylabels_left =False
gl.ylabels_right= True
gl.xlines
= LONGITUDE_FORMATTER
gl.xformatter = LATITUDE_FORMATTER
gl.yformatter
# Plot the river reach centerpoint locations
=reach_lat[idx], color='c')
ax.scatter(reach_lon[idx], y
# Add the title
f'{RIVER_NAME} Reach Centerpoint Locations') plt.title(
Text(0.5, 1.0, 'Rhine Reach Centerpoint Locations')
Plot Discharge Timeseries
The main data of interest in the results files is the timeseries of river discharge (q) estimates produced by each module. The SoS is a global dataset organized by continents and not every reach will have an associated discharge for each module. So it is helpful to filter out missing values in order to isolate and visualize discharge for the various modules.
How to locate data amongst missing values
You can use the missing_value
NetCDF variable attribute to locate the value used to indicate missing data. You can then filter on that value to isolate the time steps with discharge estimates. The following example uses the HiVDI algorithm results to demonstrate filtering missing values and plotting discharge.
# Retrieve discharge from discharge algorithm group
= results[DISCHARGE_ALGORITHM][DISCHARGE_VARIABLE][:]
discharge_algo_q
# Save the missing value
= results[DISCHARGE_ALGORITHM][DISCHARGE_VARIABLE].missing_value
missing
# Loop through each reach and filter out places where the missing value is present
= []
data_indexes for i in range(discharge_algo_q.shape[0]):
if discharge_algo_q[i].shape[0] > 1:
if np.any(discharge_algo_q[i] != missing): data_indexes.append(i) # For multiple time steps with non-missing values
if discharge_algo_q[i].shape[0] == 1 and discharge_algo_q[i] != missing: data_indexes.append(i) # For one time step with non-missing value
# Display the numeric indexes where discharge data is present
print(f"10 indexes for locations that have values:\n {data_indexes[:10]}")
10 indexes for locations that have values:
[12635, 12636, 12639, 12769, 12773, 12919, 12922, 12925, 12930, 12931]
= results['reaches']['reach_id'][data_indexes]
reach_identifiers print(f"10 reach identifiers for locations that have values:\n {reach_identifiers[:10]}")
10 reach identifiers for locations that have values:
[23261000571 23261000581 23261000631 23262000011 23262000051 23263000021
23263000051 23263000081 23263000131 23263000141]
You can now use the data indexes to retrieve location, time, and river name data about the reaches that have discharge data.
# Review what river names are present in the data
print("10 River Names")
print(river_names[data_indexes[:10]])
= np.where(river_names == RIVER_NAME)
river_indexes print(f"\nIndexes for the {RIVER_NAME}")
print(river_indexes)
# Locate overlap
= np.intersect1d(data_indexes, river_indexes)
overlap_indexes print(f"\nOverlapping indexes for the {RIVER_NAME} with {DISCHARGE_ALGORITHM.upper()} Discharge data")
print(overlap_indexes)
10 River Names
['Rhine' 'Rhine' 'Rhine' 'Rhine' 'Mosel' 'Rhine' 'Lahn; Rhine' 'Rhine'
'Lahn' 'Rhine']
Indexes for the Rhine
(array([12597, 12598, 12599, 12600, 12601, 12602, 12603, 12606, 12616,
12617, 12618, 12619, 12620, 12621, 12622, 12623, 12624, 12625,
12626, 12627, 12628, 12629, 12630, 12631, 12634, 12635, 12636,
12638, 12639, 12640, 12769, 12918, 12919, 12920, 12923, 12924,
12925, 12926, 12931, 12932, 12933, 12938, 12939, 12940, 12941,
12942, 12943, 12944, 13098, 13099, 13100, 13101, 13102, 13152,
13153, 13154, 13155, 13156, 13157, 13158, 13159, 13160, 13161,
13162, 13163, 13164, 13165, 13166, 13168, 13169, 13170, 13172,
13173, 13174, 13175, 13176, 13177, 13178, 13179, 13180, 13181,
13182, 13183, 13184, 13185, 13186, 13187, 13188, 13189, 13190,
13191, 13192, 13193, 13194, 13195, 13196, 13197, 13198, 13199,
13200, 13201, 13202, 13204, 13205, 13206, 13207, 13208, 13316,
13317, 13319, 13324, 13325, 13326, 13327, 13328, 13329, 13330,
13331, 13332, 13333, 13334, 13335, 13336, 13337, 13339, 13340,
13341, 13342, 13343, 13345, 13346, 13362, 13363, 13364, 13365,
13366, 13367, 13368, 13369, 13370, 13371, 13372, 13373, 13374,
13375, 13385]),)
Overlapping indexes for the Rhine with HIVDI Discharge data
[12635 12636 12639 12769 12919 12925 12931 12932 12933 12938 12939 12940
12941 12942 12943 13098 13099 13100 13101 13102 13152 13153 13154 13155
13156 13157 13158 13159 13161 13162 13163 13164 13165 13168 13169 13174
13178 13180 13181 13189 13190 13191 13193 13195 13197 13207]
# Select the first reach from the overlapping indexes
= overlap_indexes[0]
data_index
# Locate the reach identifier
= reaches['reach_id'][data_index]
reach_id print(f"{RIVER_NAME} reach identifier to plot: {reach_id}")
# Retrieve discharge
= discharge_algo_q[data_index]
discharge_algo_q print(f"\nDischarge for {RIVER_NAME} reach identifier # {reach_id}")
print(discharge_algo_q)
# Retrieve time
= results['reaches']['time'][data_index]
time print(f"\nTime for {RIVER_NAME} reach identifier # {reach_id}")
print(results['reaches']['time'][data_index])
Rhine reach identifier to plot: 23261000571
Discharge for Rhine reach identifier # 23261000571
[-1.00000000e+12 -1.00000000e+12 -1.00000000e+12 -1.00000000e+12
-1.00000000e+12 -1.00000000e+12 -1.00000000e+12 -1.00000000e+12
-1.00000000e+12 5.40782194e+01 4.99487494e+01 3.42913882e+01
3.44913537e+01]
Time for Rhine reach identifier # 23261000571
[7.34223036e+08 7.34308874e+08 7.34394712e+08 7.34909740e+08
7.34995578e+08 7.35081416e+08 7.35167254e+08 7.35253092e+08
7.35338929e+08 7.35510605e+08 7.35596443e+08 7.35682281e+08
7.35768119e+08]
# Transform time to correct format
= datetime.datetime(2000,1,1,0,0,0)
swot_ts = results['reaches']['time'].missing_value
missing_time = []
time_str for t in time:
if t == missing_time:
'NO_DATA')
time_str.append(else:
+ datetime.timedelta(seconds=t)).strftime('%Y-%m-%dT%H:%M:%S'))
time_str.append((swot_ts = np.array(time_str)
time_str print(f"Formatted time: {time_str}")
Formatted time: ['2023-04-07T22:50:35' '2023-04-08T22:41:13' '2023-04-09T22:31:52'
'2023-04-15T21:35:40' '2023-04-16T21:26:18' '2023-04-17T21:16:56'
'2023-04-18T21:07:33' '2023-04-19T20:58:11' '2023-04-20T20:48:49'
'2023-04-22T20:30:05' '2023-04-23T20:20:43' '2023-04-24T20:11:21'
'2023-04-25T20:01:58']
# Filter any missing values out of reach identifier discharge and time
= np.where(discharge_algo_q != missing)
missing_reach_index
= discharge_algo_q[missing_reach_index]
discharge_algo_q print(f"Discharge for {RIVER_NAME} reach identfier # {reach_id}")
print(discharge_algo_q)
= time_str[missing_reach_index]
time_str print(f"\nTime for {RIVER_NAME} reach identfier # {reach_id}")
print(time_str)
Discharge for Rhine reach identfier # 23261000571
[54.07821944 49.94874936 34.29138816 34.49135369]
Time for Rhine reach identfier # 23261000571
['2023-04-22T20:30:05' '2023-04-23T20:20:43' '2023-04-24T20:11:21'
'2023-04-25T20:01:58']
# Plot Discharge for the River Reach Identifier
# Set up plot
= plt.figure(figsize=(10,5))
fig = plt.subplot(311)
ax1
# Plot data
ax1.scatter(time_str, discharge_algo_q)
ax1.plot(time_str, discharge_algo_q)
# Define labels and title
'Discharge')
ax1.set_ylabel('Time')
ax1.set_xlabel(= 45)
plt.xticks(rotation
f"Discharge Timeseries from HIVDI for the Ohio River reach identifier: {reach_id}.") plt.suptitle(
Text(0.5, 0.98, 'Discharge Timeseries from HIVDI for the Ohio River reach identifier: 23261000571.')
Plotting integrator results for comparison
The SoS contains reach-level Flow Law Parameter (FLPE) algorithms: HiVDI, neoBAM, MetroMan, MOMMA, SAD, SIC4DVar that produce discharge estimates using SWOT observations, SoS Priors and SWORD data. It can be helpful to compare the reach-level FLPEs to the discharge values produced by the Mean Optimization Integrator (MOI). The MOI takes SWOT observation data and reach-level FLPE output and integrates the results. It uses river topology to force mass conservation and also defined uncertainty.
# Locate MOI discharge results for discharge algorithm making sure to filter out missing values
= results["moi"][DISCHARGE_ALGORITHM]["q"][data_index]
moi_q = moi_q[missing_reach_index]
moi_q
print(f"{DISCHARGE_ALGORITHM.upper()} MOI Discharge for {RIVER_NAME} reach identfier # {reach_id}")
print(moi_q)
HIVDI MOI Discharge for Rhine reach identfier # 23261000571
[0.0009796 0.00062748 0.00012596 0.00014881]
# Plot discharge algorithm alongside MOI discharge
# Discharge algorithm Q
plt.scatter(time_str, discharge_algo_q)=f"{DISCHARGE_ALGORITHM.upper()}")
plt.plot(time_str, discharge_algo_q, label
# MOI Q
plt.scatter(time_str, moi_q)="MOI")
plt.plot(time_str, moi_q, label
f"Discharge Timeseries from HIVDI for the {RIVER_NAME} reach identifier: {reach_id}.")
plt.suptitle(
plt.legend() plt.tight_layout()
Close dataset and file handler references
priors.close() results.close()
Disclaimer: Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise, does not constitute or imply its endorsement by the United States Government or the Jet Propulsion Laboratory, California Institute of Technology.