From c71d8fb3019f6706ad8824e9ebc0c233d3e20fa5 Mon Sep 17 00:00:00 2001 From: joshdpaul Date: Tue, 9 Jun 2026 15:31:44 -0800 Subject: [PATCH 1/2] initial commit for stream temp endpoints; still need documentation; need to update clim coverage to fix units and stats coverage to fix NaN issue --- csv_functions.py | 193 +++++++++++++++++++++----------- routes/arctic_hydrology.py | 221 ++++++++++++++++++++++++++++++++++--- 2 files changed, 333 insertions(+), 81 deletions(-) diff --git a/csv_functions.py b/csv_functions.py index b6d255a5..71c65801 100644 --- a/csv_functions.py +++ b/csv_functions.py @@ -1558,75 +1558,136 @@ def arctic_hydrology_csv(data, filename_prefix, source_metadata): metadata = "" if "Statistics" in filename_prefix: - if isinstance(source_metadata, str) and source_metadata in source_notes: - metadata += f"# The following hydrologic statistics are calculated from modeled daily streamflow data. {source_notes[source_metadata]}\n" + if "Water Temperature" in filename_prefix: + if isinstance(source_metadata, str) and source_metadata in source_notes: + metadata += f"# The following water temperature statistics are calculated from modeled daily stream temperature data. {source_notes[source_metadata]}\n" + else: + metadata += "# The following water temperature statistics are calculated from modeled daily stream temperature data:\n" + metadata += "# wt_days_gt13_mean: Mean annual count of days with stream temperature > 13°C (days/year).\n" + metadata += "# wt_days_gt18_mean: Mean annual count of days with stream temperature > 18°C (days/year).\n" + metadata += "# wt_days_gt20_mean: Mean annual count of days with stream temperature > 20°C (days/year).\n" + metadata += "# wt_mean_jan: Mean of monthly mean stream temperatures for January (°C).\n" + metadata += "# wt_mean_feb: Mean of monthly mean stream temperatures for February (°C).\n" + metadata += "# wt_mean_mar: Mean of monthly mean stream temperatures for March (°C).\n" + metadata += "# wt_mean_apr: Mean of monthly mean stream temperatures for April (°C).\n" + metadata += "# wt_mean_may: Mean of monthly mean stream temperatures for May (°C).\n" + metadata += "# wt_mean_jun: Mean of monthly mean stream temperatures for June (°C).\n" + metadata += "# wt_mean_jul: Mean of monthly mean stream temperatures for July (°C).\n" + metadata += "# wt_mean_aug: Mean of monthly mean stream temperatures for August (°C).\n" + metadata += "# wt_mean_sep: Mean of monthly mean stream temperatures for September (°C).\n" + metadata += "# wt_mean_oct: Mean of monthly mean stream temperatures for October (°C).\n" + metadata += "# wt_mean_nov: Mean of monthly mean stream temperatures for November (°C).\n" + metadata += "# wt_mean_dec: Mean of monthly mean stream temperatures for December (°C).\n" + metadata += "# wt_min_jan: Minimum monthly mean stream temperature for January across all years in era (°C).\n" + metadata += "# wt_min_feb: Minimum monthly mean stream temperature for February across all years in era (°C).\n" + metadata += "# wt_min_mar: Minimum monthly mean stream temperature for March across all years in era (°C).\n" + metadata += "# wt_min_apr: Minimum monthly mean stream temperature for April across all years in era (°C).\n" + metadata += "# wt_min_may: Minimum monthly mean stream temperature for May across all years in era (°C).\n" + metadata += "# wt_min_jun: Minimum monthly mean stream temperature for June across all years in era (°C).\n" + metadata += "# wt_min_jul: Minimum monthly mean stream temperature for July across all years in era (°C).\n" + metadata += "# wt_min_aug: Minimum monthly mean stream temperature for August across all years in era (°C).\n" + metadata += "# wt_min_sep: Minimum monthly mean stream temperature for September across all years in era (°C).\n" + metadata += "# wt_min_oct: Minimum monthly mean stream temperature for October across all years in era (°C).\n" + metadata += "# wt_min_nov: Minimum monthly mean stream temperature for November across all years in era (°C).\n" + metadata += "# wt_min_dec: Minimum monthly mean stream temperature for December across all years in era (°C).\n" + metadata += "# wt_max_jan: Maximum monthly mean stream temperature for January across all years in era (°C).\n" + metadata += "# wt_max_feb: Maximum monthly mean stream temperature for February across all years in era (°C).\n" + metadata += "# wt_max_mar: Maximum monthly mean stream temperature for March across all years in era (°C).\n" + metadata += "# wt_max_apr: Maximum monthly mean stream temperature for April across all years in era (°C).\n" + metadata += "# wt_max_may: Maximum monthly mean stream temperature for May across all years in era (°C).\n" + metadata += "# wt_max_jun: Maximum monthly mean stream temperature for June across all years in era (°C).\n" + metadata += "# wt_max_jul: Maximum monthly mean stream temperature for July across all years in era (°C).\n" + metadata += "# wt_max_aug: Maximum monthly mean stream temperature for August across all years in era (°C).\n" + metadata += "# wt_max_sep: Maximum monthly mean stream temperature for September across all years in era (°C).\n" + metadata += "# wt_max_oct: Maximum monthly mean stream temperature for October across all years in era (°C).\n" + metadata += "# wt_max_nov: Maximum monthly mean stream temperature for November across all years in era (°C).\n" + metadata += "# wt_max_dec: Maximum monthly mean stream temperature for December across all years in era (°C).\n" + metadata += "# wt_ann_max_temp_mean: Mean annual maximum daily stream temperature (°C).\n" + metadata += "# wt_ann_max_temp_doy_mean: Mean Julian day of the annual maximum daily stream temperature (day of year).\n" + metadata += "# wt_7d_max_temp_mean: Mean annual maximum 7-day rolling average stream temperature (°C).\n" + metadata += "# wt_7d_max_temp_doy_mean: Mean Julian day of the center of the annual maximum 7-day rolling average (day of year).\n" + metadata += "# wt_cdd_may_sept_mean: Mean annual cumulative degree days above 0°C, May through September (°C days).\n" else: - metadata += "# The following hydrologic statistics are calculated from modeled daily streamflow data:\n" - metadata += "# dh1: Mean annual maximum 1-day average flow (cubic feet per second - temporal).\n" - metadata += "# dh2: Mean annual maximum 3-day average flow (cubic feet per second - temporal).\n" - metadata += "# dh3: Mean annual maximum 7-day average flow (cubic feet per second - temporal).\n" - metadata += "# dh4: Mean annual maximum 30-day average flow (cubic feet per second - temporal).\n" - metadata += "# dh5: Mean annual maximum 90-day average flow (cubic feet per second - temporal).\n" - metadata += "# dh15: Median annual average duration of high flow pulses above the 75th percentile (days/year - temporal).\n" - metadata += "# dl1: Mean annual minimum 1-day average flow (cubic feet per second - temporal).\n" - metadata += "# dl2: Mean annual minimum 3-day average flow (cubic feet per second - temporal).\n" - metadata += "# dl3: Mean annual minimum 7-day average flow (cubic feet per second - temporal).\n" - metadata += "# dl4: Mean annual minimum 30-day average flow (cubic feet per second - temporal).\n" - metadata += "# dl5: Mean annual minimum 90-day average flow (cubic feet per second - temporal).\n" - metadata += "# dl16: Median annual average duration of low flow pulses below the 25th percentile (days/year - temporal).\n" - metadata += "# lf1: Median annual number of days below a threshold of 0.1 cubic feet per second per square mile (days/year - temporal).\n" - metadata += "# spr_dur3: Median spring (April-June) maximum of 3-day moving average flows (cubic feet per second - temporal).\n" - metadata += "# spr_dur7: Median spring (April-June) maximum of 7-day moving average flows (cubic feet per second - temporal).\n" - metadata += "# sum_dur3: Median summer (July-September) minimum of 3-day moving average flow (cubic feet per second - temporal).\n" - metadata += "# sum_dur7: Median summer (July-September) minimum of 7-day moving average flow (cubic feet per second - temporal).\n" - metadata += "# fh1: Mean annual count of high flow pulses above the 75th percentile (number of events/year - temporal).\n" - metadata += "# fh5: Mean annual flood frequency above median flow (number of events/year - temporal).\n" - metadata += "# fh6: Mean annual flood frequency above 3 times median flow (number of events/year - temporal).\n" - metadata += "# fh7: Mean annual flood frequency above 7 times median flow (number of events/year - temporal).\n" - metadata += "# fl1: Mean annual count of low flow pulses below the 25th percentile (number of events/year - temporal).\n" - metadata += "# fl3: Mean annual count of events below 5 percent of mean flow (number of events/year - temporal).\n" - metadata += "# spr_freq: Median spring (April-June) count of flow events above the 10th percentile of the full record (number of events/year - temporal).\n" - metadata += "# sum_freq: Median summer (July-September) count of flow events below the 90th percentile of the full record (number of events/year - temporal).\n" - metadata += "# ma3: Coefficient of variation (standard deviation/mean) of annual daily flows; mean of annual CVs (percent - temporal).\n" - metadata += "# ma4: Standard deviation of percentiles of log-transformed flow divided by mean of those percentiles (percent - spatial).\n" - metadata += "# ma12: Mean of monthly flow values for January (cubic feet per second - temporal).\n" - metadata += "# ma13: Mean of monthly flow values for February (cubic feet per second - temporal).\n" - metadata += "# ma14: Mean of monthly flow values for March (cubic feet per second - temporal).\n" - metadata += "# ma15: Mean of monthly flow values for April (cubic feet per second - temporal).\n" - metadata += "# ma16: Mean of monthly flow values for May (cubic feet per second - temporal).\n" - metadata += "# ma17: Mean of monthly flow values for June (cubic feet per second - temporal).\n" - metadata += "# ma18: Mean of monthly flow values for July (cubic feet per second - temporal).\n" - metadata += "# ma19: Mean of monthly flow values for August (cubic feet per second - temporal).\n" - metadata += "# ma20: Mean of monthly flow values for September (cubic feet per second - temporal).\n" - metadata += "# ma21: Mean of monthly flow values for October (cubic feet per second - temporal).\n" - metadata += "# ma22: Mean of monthly flow values for November (cubic feet per second - temporal).\n" - metadata += "# ma23: Mean of monthly flow values for December (cubic feet per second - temporal).\n" - metadata += "# ma99: Mean of monthly flow values for the entire year. Compute the mean of the monthly mean flows for each month of the year. MA99 is the mean of these 12 values (cubic feet per second - temporal).\n" - metadata += "# mh14: Median of annual (maximum flow / median annual flow) ratios (dimensionless - temporal).\n" - metadata += "# mh20: Mean annual maximum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" - metadata += "# ml17: Base flow index: mean of annual (7-day minimum flow / mean annual flow) ratios (dimensionless - temporal).\n" - metadata += "# spr_mag: Median spring (April-June) maximum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" - metadata += "# sum_cv: Median annual coefficient of variation of summer (July-September) daily flows (percent - temporal).\n" - metadata += "# sum_mag: Median summer (July-September) minimum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" - metadata += "# ra1: Mean rise rate: mean of positive daily flow changes (cubic feet per second/day - temporal).\n" - metadata += "# ra3: Mean fall rate: mean of negative daily flow changes (cubic feet per second/day - temporal).\n" - metadata += "# ra8: Median annual number of flow direction reversals (days - temporal).\n" - metadata += "# spr_ord: Median Julian date of spring (April-June) maximum flow (Julian day - temporal).\n" - metadata += "# sum_ord: Median Julian date of summer (July-September) minimum flow (Julian day - temporal).\n" - metadata += "# th1: Median Julian date of annual maximum flow (Julian day - temporal).\n" - metadata += "# tl1: Median Julian date of annual minimum flow (Julian day - temporal).\n" + if isinstance(source_metadata, str) and source_metadata in source_notes: + metadata += f"# The following hydrologic statistics are calculated from modeled daily streamflow data. {source_notes[source_metadata]}\n" + else: + metadata += "# The following hydrologic statistics are calculated from modeled daily streamflow data:\n" + metadata += "# dh1: Mean annual maximum 1-day average flow (cubic feet per second - temporal).\n" + metadata += "# dh2: Mean annual maximum 3-day average flow (cubic feet per second - temporal).\n" + metadata += "# dh3: Mean annual maximum 7-day average flow (cubic feet per second - temporal).\n" + metadata += "# dh4: Mean annual maximum 30-day average flow (cubic feet per second - temporal).\n" + metadata += "# dh5: Mean annual maximum 90-day average flow (cubic feet per second - temporal).\n" + metadata += "# dh15: Median annual average duration of high flow pulses above the 75th percentile (days/year - temporal).\n" + metadata += "# dl1: Mean annual minimum 1-day average flow (cubic feet per second - temporal).\n" + metadata += "# dl2: Mean annual minimum 3-day average flow (cubic feet per second - temporal).\n" + metadata += "# dl3: Mean annual minimum 7-day average flow (cubic feet per second - temporal).\n" + metadata += "# dl4: Mean annual minimum 30-day average flow (cubic feet per second - temporal).\n" + metadata += "# dl5: Mean annual minimum 90-day average flow (cubic feet per second - temporal).\n" + metadata += "# dl16: Median annual average duration of low flow pulses below the 25th percentile (days/year - temporal).\n" + metadata += "# lf1: Median annual number of days below a threshold of 0.1 cubic feet per second per square mile (days/year - temporal).\n" + metadata += "# spr_dur3: Median spring (April-June) maximum of 3-day moving average flows (cubic feet per second - temporal).\n" + metadata += "# spr_dur7: Median spring (April-June) maximum of 7-day moving average flows (cubic feet per second - temporal).\n" + metadata += "# sum_dur3: Median summer (July-September) minimum of 3-day moving average flow (cubic feet per second - temporal).\n" + metadata += "# sum_dur7: Median summer (July-September) minimum of 7-day moving average flow (cubic feet per second - temporal).\n" + metadata += "# fh1: Mean annual count of high flow pulses above the 75th percentile (number of events/year - temporal).\n" + metadata += "# fh5: Mean annual flood frequency above median flow (number of events/year - temporal).\n" + metadata += "# fh6: Mean annual flood frequency above 3 times median flow (number of events/year - temporal).\n" + metadata += "# fh7: Mean annual flood frequency above 7 times median flow (number of events/year - temporal).\n" + metadata += "# fl1: Mean annual count of low flow pulses below the 25th percentile (number of events/year - temporal).\n" + metadata += "# fl3: Mean annual count of events below 5 percent of mean flow (number of events/year - temporal).\n" + metadata += "# spr_freq: Median spring (April-June) count of flow events above the 10th percentile of the full record (number of events/year - temporal).\n" + metadata += "# sum_freq: Median summer (July-September) count of flow events below the 90th percentile of the full record (number of events/year - temporal).\n" + metadata += "# ma3: Coefficient of variation (standard deviation/mean) of annual daily flows; mean of annual CVs (percent - temporal).\n" + metadata += "# ma4: Standard deviation of percentiles of log-transformed flow divided by mean of those percentiles (percent - spatial).\n" + metadata += "# ma12: Mean of monthly flow values for January (cubic feet per second - temporal).\n" + metadata += "# ma13: Mean of monthly flow values for February (cubic feet per second - temporal).\n" + metadata += "# ma14: Mean of monthly flow values for March (cubic feet per second - temporal).\n" + metadata += "# ma15: Mean of monthly flow values for April (cubic feet per second - temporal).\n" + metadata += "# ma16: Mean of monthly flow values for May (cubic feet per second - temporal).\n" + metadata += "# ma17: Mean of monthly flow values for June (cubic feet per second - temporal).\n" + metadata += "# ma18: Mean of monthly flow values for July (cubic feet per second - temporal).\n" + metadata += "# ma19: Mean of monthly flow values for August (cubic feet per second - temporal).\n" + metadata += "# ma20: Mean of monthly flow values for September (cubic feet per second - temporal).\n" + metadata += "# ma21: Mean of monthly flow values for October (cubic feet per second - temporal).\n" + metadata += "# ma22: Mean of monthly flow values for November (cubic feet per second - temporal).\n" + metadata += "# ma23: Mean of monthly flow values for December (cubic feet per second - temporal).\n" + metadata += "# ma99: Mean of monthly flow values for the entire year. Compute the mean of the monthly mean flows for each month of the year. MA99 is the mean of these 12 values (cubic feet per second - temporal).\n" + metadata += "# mh14: Median of annual (maximum flow / median annual flow) ratios (dimensionless - temporal).\n" + metadata += "# mh20: Mean annual maximum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" + metadata += "# ml17: Base flow index: mean of annual (7-day minimum flow / mean annual flow) ratios (dimensionless - temporal).\n" + metadata += "# spr_mag: Median spring (April-June) maximum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" + metadata += "# sum_cv: Median annual coefficient of variation of summer (July-September) daily flows (percent - temporal).\n" + metadata += "# sum_mag: Median summer (July-September) minimum flow divided by drainage area (cubic feet per second/square mile - temporal).\n" + metadata += "# ra1: Mean rise rate: mean of positive daily flow changes (cubic feet per second/day - temporal).\n" + metadata += "# ra3: Mean fall rate: mean of negative daily flow changes (cubic feet per second/day - temporal).\n" + metadata += "# ra8: Median annual number of flow direction reversals (days - temporal).\n" + metadata += "# spr_ord: Median Julian date of spring (April-June) maximum flow (Julian day - temporal).\n" + metadata += "# sum_ord: Median Julian date of summer (July-September) minimum flow (Julian day - temporal).\n" + metadata += "# th1: Median Julian date of annual maximum flow (Julian day - temporal).\n" + metadata += "# tl1: Median Julian date of annual minimum flow (Julian day - temporal).\n" else: - if isinstance(source_metadata, str) and source_metadata in source_notes: - metadata += f"# Climatologies are calculated from modeled daily streamflow data. {source_notes[source_metadata]}\n" + if "Water Temperature" in filename_prefix: + if isinstance(source_metadata, str) and source_metadata in source_notes: + metadata += f"# Climatologies are calculated from modeled daily stream temperature data. {source_notes[source_metadata]}\n" + else: + metadata += "# Climatologies are calculated from modeled daily stream temperature data.\n" + metadata += "# doy is the day of year (1-366) for which the climatology value is reported.\n" + metadata += "# water_year_index is the water year index (1-366) for which the climatology value is reported. The water year is defined as starting on October 1 (DOY 275 in a 366 day year = water year index 1) and ending September 30 (DOY 274 in a 366 day year = water year index 366).\n" + metadata += "# doy_min is the minimum stream temperature for the given day of year across all years in the era (°C).\n" + metadata += "# doy_mean is the mean stream temperature for the given day of year across all years in the era (°C).\n" + metadata += "# doy_max is the maximum stream temperature for the given day of year across all years in the era (°C).\n" else: - metadata += ( - "# Climatologies are calculated from modeled daily streamflow data.\n" - ) - metadata += "# doy is the day of year (1-366) for which the climatology value is reported. \n" - metadata += "# water_year_index is the water year index (1-366) for which the climatology value is reported. The water year is defined as starting on October 1 (DOY 275 in a 366 day year = water year index 1) and ending September 30 (DOY 274 in a 366 day year = water year index 366).\n" - metadata += "# doy_min is the minimum streamflow value for the given day of year across all years in the era (cubic feet per second).\n" - metadata += "# doy_mean is the mean streamflow value for the given day of year across all years in the era (cubic feet per second).\n" - metadata += "# doy_max is the maximum streamflow value for the given day of year across all years in the era (cubic feet per second).\n" + if isinstance(source_metadata, str) and source_metadata in source_notes: + metadata += f"# Climatologies are calculated from modeled daily streamflow data. {source_notes[source_metadata]}\n" + else: + metadata += ( + "# Climatologies are calculated from modeled daily streamflow data.\n" + ) + metadata += "# doy is the day of year (1-366) for which the climatology value is reported. \n" + metadata += "# water_year_index is the water year index (1-366) for which the climatology value is reported. The water year is defined as starting on October 1 (DOY 275 in a 366 day year = water year index 1) and ending September 30 (DOY 274 in a 366 day year = water year index 366).\n" + metadata += "# doy_min is the minimum streamflow value for the given day of year across all years in the era (cubic feet per second).\n" + metadata += "# doy_mean is the mean streamflow value for the given day of year across all years in the era (cubic feet per second).\n" + metadata += "# doy_max is the maximum streamflow value for the given day of year across all years in the era (cubic feet per second).\n" metadata += "# model is the global climate model.\n" metadata += "# era is the time period over which data are summarized.\n" diff --git a/routes/arctic_hydrology.py b/routes/arctic_hydrology.py index 752f8650..34771601 100644 --- a/routes/arctic_hydrology.py +++ b/routes/arctic_hydrology.py @@ -31,6 +31,8 @@ coverages = { "stats": ["ak_hydro_segments_mhit_stats_combined"], "doy_climatology": ["ak_hydro_segments_doy_climatology"], + "wt_stats": ["ak_hydro_segments_wt_stats"], + "doy_wt_climatology": ["ak_hydro_segments_doy_wt_climatology"], } stat_source_encodings = { @@ -527,7 +529,57 @@ def calculate_and_apply_gcm_diffs_to_cheng_climatology(data_dict): return adjusted_data_dict -def package_metadata(ds, data_dict, source=None): +def calculate_and_apply_gcm_diffs_to_cheng_wt_climatology(data_dict): + """ + Applies GCM-projected water temperature changes to the historical Cheng climatology + using additive differences rather than the multiplicative ratios used for streamflow. + + Temperature deltas must be additive because water temperature is measured on an + absolute scale (C). The upstream processing in the arctic_rivers repo + (calculate_wt_stats.py) uses the same additive approach: + gcm_diff = future − past + gcm_diff_applied_to_cheng = cheng_historical + gcm_diff + + Models without a '1990-2021' era (e.g. PGWh, PGWm) are absent from the returned + dict entirely — they receive no entry, not even unadjusted values. A hydroviz-style + endpoint that needs PGW coverage should backfill them from original_gcm after calling + this function (see the streamflow hydroviz handler for the established pattern). + Args: + data_dict (dict): DOY climatology data dict keyed by model then era + Returns: + dict: Adjusted data dict with Cheng baseline applied to all eligible models. + """ + adjusted_data_dict = {} + for model in data_dict.keys(): + if model == "historical": + adjusted_data_dict[model] = data_dict[model] + continue + if "1990-2021" not in data_dict[model]: + continue + adjusted_data_dict[model] = {} + for era in data_dict[model].keys(): + if era == "1990-2021": + continue + adjusted_data_dict[model][era] = [] + for i in range(len(data_dict[model][era])): + entry = data_dict[model][era][i] + doy_stats = { + "doy": entry["doy"], + "water_year_index": entry["water_year_index"], + } + for stat in entry.keys(): + if stat in ("doy", "water_year_index"): + continue + cheng_historical = data_dict["historical"]["1990-2021"][i][stat] + gcm_historical = data_dict[model]["1990-2021"][i][stat] + gcm_projected = entry[stat] + gcm_delta = gcm_projected - gcm_historical + doy_stats[stat] = round(cheng_historical + gcm_delta, 3) + adjusted_data_dict[model][era].append(doy_stats) + return adjusted_data_dict + + +def package_metadata(ds, data_dict, source=None, var_context="streamflow"): """ Function to package the metadata from the dataset into the data dictionary. Args: @@ -578,21 +630,20 @@ def package_metadata(ds, data_dict, source=None): # "doy" vars from hydrograph datasets if var in ["doy_min", "doy_mean", "doy_max"]: - data_dict["metadata"]["variables"][var]["units"] = "cfs" - if var == "doy_min": - op = "Minimum" - elif var == "doy_mean": - op = "Mean" - else: - op = "Maximum" - data_dict["metadata"]["variables"][var][ - "description" - ] = f"{op} streamflow value (cfs) on the specified day of year, aggregated over all years in the era." - # add source notes - if source in source_notes: + if var_context == "streamflow": + op = ( + "Minimum" + if var == "doy_min" + else ("Mean" if var == "doy_mean" else "Maximum") + ) + data_dict["metadata"]["variables"][var]["units"] = "cfs" data_dict["metadata"]["variables"][var][ "description" - ] += f" {source_notes[source]}" + ] = f"{op} streamflow value (cfs) on the specified day of year, aggregated over all years in the era." + if source in source_notes: + data_dict["metadata"]["variables"][var][ + "description" + ] += f" {source_notes[source]}" # also add doy and water_year_index metadata: # these will be overwritten multiple times, but the values are the same for all three vars and we will only see them once in the final output @@ -802,6 +853,146 @@ def run_get_arctic_hydrology_modeled_climatology(stream_id): return render_template("500/server_error.html"), 500 +@routes.route("/arctic_hydrology/wt_stats/") +def run_get_arctic_hydrology_wt_stats_data(stream_id): + """ + Function to fetch water temperature statistics from Rasdaman for a single stream ID. + Example URL: http://localhost:5000/arctic_hydrology/wt_stats/81000004 + Args: + stream_id (str): Stream ID for the hydrology data + Returns: + JSON response with water temperature statistics for the requested stream ID. + """ + source = request.args.get("source", None) + if source is None: + source = "gcm_diff_applied_to_cheng" + + if not stream_id.isdigit(): + return render_template("400/bad_request.html"), 400 + + gdf = asyncio.run(get_features(stream_id)) + if isinstance(gdf, tuple): + return gdf + + try: + decode_dict = asyncio.run( + get_decode_dicts_from_axis_attributes(coverages["wt_stats"]) + )[0] + + ds = asyncio.run( + fetch_hydro_data( + coverages["wt_stats"], stream_id, source=stat_source_encodings[source] + ) + )[0] + + for dim, mapping in decode_dict.items(): + if dim == "source": + continue + ds = ds.assign_coords({dim: [mapping[int(v)] for v in ds[dim].values]}) + + try: + data_dict = package_stats_data(stream_id, ds) + except Exception: + return render_template("500/server_error.html"), 500 + + data_dict = package_metadata(ds, data_dict, source=source) + data_dict = populate_feature_attributes(data_dict, gdf) + data_dict = prune_nulls_with_max_intensity(data_dict) + + if request.args.get("format") == "csv": + try: + return create_csv( + data=data_dict, + endpoint="arctic_hydrology", + filename_prefix="Water Temperature Statistics", + place_id=stream_id, + lat=str(data_dict["latitude"]), + lon=str(data_dict["longitude"]), + source_metadata=source, + ) + except Exception: + return render_template("500/server_error.html"), 500 + + return jsonify(data_dict) + + except Exception as exc: + if hasattr(exc, "status") and exc.status == 404: + return render_template("404/no_data.html"), 404 + return render_template("500/server_error.html"), 500 + + +@routes.route("/arctic_hydrology/wt_modeled_climatology/") +def run_get_arctic_hydrology_wt_modeled_climatology(stream_id): + """ + Function to fetch water temperature DOY climatology from Rasdaman for a single stream ID. + Example URL: http://localhost:5000/arctic_hydrology/wt_modeled_climatology/81000004 + Args: + stream_id (str): Stream ID for the hydrology data + Returns: + JSON response with modeled daily water temperature climatology for the requested stream ID. + """ + source = request.args.get("source", None) + if source is None: + source = "gcm_diff_applied_to_cheng" + elif source == "gcm_diff": + return render_template("400/bad_request.html"), 400 + + if not stream_id.isdigit(): + return render_template("400/bad_request.html"), 400 + + gdf = asyncio.run(get_features(stream_id)) + if isinstance(gdf, tuple): + return gdf + + try: + datasets = asyncio.run( + fetch_hydro_data(coverages["doy_wt_climatology"], stream_id) + ) + decode_dicts = asyncio.run( + get_decode_dicts_from_axis_attributes(coverages["doy_wt_climatology"]) + ) + + decoded_datasets = [] + for ds, decode_dict in zip(datasets, decode_dicts): + for dim, mapping in decode_dict.items(): + ds = ds.assign_coords({dim: [mapping[int(v)] for v in ds[dim].values]}) + decoded_datasets.append(ds) + datasets = decoded_datasets + + data_dict = package_hydrograph_data(stream_id, datasets) + data_dict = package_metadata( + datasets[0], data_dict, source=source, var_context="water_temperature" + ) + data_dict = populate_feature_attributes(data_dict, gdf) + data_dict = prune_nulls_with_max_intensity(data_dict) + + if request.args.get("format") == "csv": + try: + return create_csv( + data=data_dict, + endpoint="arctic_hydrology", + filename_prefix="Modeled Water Temperature Climatologies", + place_id=stream_id, + lat=str(data_dict["latitude"]), + lon=str(data_dict["longitude"]), + source_metadata=source, + ) + except Exception: + return render_template("500/server_error.html"), 500 + + if source == "gcm_diff_applied_to_cheng": + data_dict["data"] = calculate_and_apply_gcm_diffs_to_cheng_wt_climatology( + data_dict["data"] + ) + + return jsonify(data_dict) + + except Exception as exc: + if hasattr(exc, "status") and exc.status == 404: + return render_template("404/no_data.html"), 404 + return render_template("500/server_error.html"), 500 + + @routes.route("/arctic_hydrology/hydroviz/") def run_get_arctic_hydrology_hydroviz(stream_id): """ @@ -1036,5 +1227,5 @@ def run_get_arctic_hydrology_hydroviz(stream_id): return jsonify(response) - except Exception as exc: + except Exception: return render_template("500/server_error.html"), 500 From 3285fb4cc28a74258377ef548d9bbc9b8f97952b Mon Sep 17 00:00:00 2001 From: joshdpaul Date: Mon, 15 Jun 2026 15:08:33 -0800 Subject: [PATCH 2/2] add documentation for stream temp endpoints and references --- templates/documentation/arctic_hydrology.html | 693 +++++++++++++++++- 1 file changed, 683 insertions(+), 10 deletions(-) diff --git a/templates/documentation/arctic_hydrology.html b/templates/documentation/arctic_hydrology.html index 22087be6..e25a13d3 100644 --- a/templates/documentation/arctic_hydrology.html +++ b/templates/documentation/arctic_hydrology.html @@ -2,16 +2,16 @@

Arctic Hydrology

- These endpoints provide access to modeled streamflow statistics and daily - streamflow climatologies for 34,346 stream network segments in Alaska and - Canada. Stream segments were derived from the MERIT Hydro network, and - streamflow was modeled using the Regional Arctic System Model (RASM) with - dynamically downscaled CMIP6 historical (1990–2021) and projected - mid-century (2034-2065) climate data using the SSP3-7.0 scenario. Four - Community Earth System Model 2 (CESM2) runs are included, as well as two - pseudo-global warming (PGW) runs based on the CESM2 Large Ensemble. A - dynamically downscaled ERA5 baseline (Cheng) is available for the historical - period. + These endpoints provide access to modeled streamflow statistics, streamflow + climatologies, water temperature statistics, and water temperature + climatologies for 34,346 stream network segments in Alaska and Canada. Stream + segments were derived from the MERIT Hydro network, and streamflow was modeled + using the Regional Arctic System Model (RASM) with dynamically downscaled + CMIP6 historical (1990–2021) and projected mid-century (2034-2065) + climate data using the SSP3-7.0 scenario. Four Community Earth System Model 2 + (CESM2) runs are included, as well as two pseudo-global warming (PGW) runs + based on the CESM2 Large Ensemble using the SSP2-4.5 scenario. A dynamically + downscaled ERA5 baseline (Cheng) is available for the historical period.

@@ -132,6 +132,114 @@

Modeled daily streamflow climatologies by stream ID

+

Modeled water temperature statistics by stream ID

+ +

+ Query water temperature statistics for all models. Statistics are computed + over historical (1990–2021) and projected mid-century (2034–2065) + eras. These statistics are defined + below. +

+ + + + + + + + + + + + + + + + + + + +
EndpointExample URL
Water temperature statistics + /arctic_hydrology/wt_stats/81000004 +
+ CSV output is also available by appending ?format=csv to + the URL.
+ The data source can be controlled with the ?source= + parameter. Accepted values are: +
    +
  • + gcm_diff_applied_to_cheng (default) — + GCM-projected changes applied to the historical Cheng baseline. PGW + models are not included. +
  • +
  • + original_gcm — raw output from the original GCM + runs. Includes PGW models. +
  • +
  • + gcm_diff — additive difference between the + projected and historical GCM runs. These are not actual temperature + values; add them to your own historical baseline to approximate + future values. PGW models are not included. +
  • +
+ Chaining of multiple arguments is supported (e.g., + ?source=original_gcm&format=csv). +
+ +

Modeled daily water temperature climatologies by stream ID

+ +

+ Query daily water temperature climatologies for all models. Useful for + constructing temperature hydrographs. Minimum, maximum, and mean values for + each day of year are computed over historical (1990–2021) and projected + mid-century (2034–2065) eras. +

+ + + + + + + + + + + + + + + + + + + +
EndpointExample URL
Modeled daily water temperature climatologies + /arctic_hydrology/wt_modeled_climatology/81000004 +
+ CSV output is also available by appending ?format=csv to + the URL.
+ The data source can be controlled with the ?source= + parameter. Accepted values are: +
    +
  • + gcm_diff_applied_to_cheng (default) — + GCM-projected changes applied to the historical Cheng baseline. PGW + models are not included. +
  • +
  • + original_gcm — raw output from the original GCM + runs. Includes PGW models. +
  • +
+ Chaining of multiple arguments is supported (e.g., + ?source=original_gcm&format=csv). +
+

Output

Modeled hydrologic statistics

@@ -374,6 +482,208 @@

Modeled daily streamflow climatologies

} +

Modeled water temperature statistics

+ +

Results from stream ID queries will look like this:

+ +
+{
+  "data": {
+    "C2LE2": {
+      "2034-2065": {
+        "wt_7d_max_temp_doy_mean": 192.47,
+        "wt_7d_max_temp_mean": 13.44,
+        ...
+        "wt_min_sep": 3.05
+      }
+    },
+    ...
+    "historical": {
+      "1990-2021": {
+        "wt_7d_max_temp_doy_mean": 196.03,
+        "wt_7d_max_temp_mean": 11.92,
+        ...
+        "wt_min_sep": 2.35
+      }
+    }
+  },
+  "id": "81000004",
+  "latitude": 70.8292,
+  "longitude": -155.42,
+  "metadata": {
+    "source": {
+      "citation": "Dylan Blaskey, Keith Musselman, Andrew Newman, & Yifan Cheng. (2024). Alaskan river discharge, temperature, and climate data for a climate reference (1990-2021) and at mid-century (2034-2065). Arctic Data Center. doi:10.18739/A25M62870."
+    },
+    "variables": {
+      "wt_7d_max_temp_mean": {
+        "description": "Mean (over era years) of the annual maximum 7-day rolling average stream temperature.",
+        "units": "degC"
+      },
+      ...
+    }
+  },
+  "watershed": "19060206",
+  "watershed_outlet": true
+}
+
+ +

+ Note that latitude and longitude values represent the approximate centroid of + the stream segment. Unlike the streamflow statistics endpoint, this response + does not include a summary key. The above output is structured + like this: +

+ +
+{
+  "data": {
+    <model>: {
+      <era>: {
+        <variable>: <value>,
+        ...
+      },
+      ...
+    },
+    ...
+  },
+  "id": <stream ID>,
+  "latitude": <latitude of stream segment>,
+  "longitude": <longitude of stream segment>,
+  "metadata": {
+    "source": {
+      "citation": <academic reference for source data>
+    },
+    "variables": {
+      <variable>: {
+        "description": <description of variable>,
+        "units": <units of variable>
+      },
+      ...
+    }
+  },
+  "watershed": <HUC8 or Yukon watershed ID>,
+  "watershed_outlet": <true if segment is a watershed outlet>
+}
+
+ +

Modeled daily water temperature climatologies

+ +

Results from stream ID queries will look like this:

+ +
+{
+  "data": {
+    "C2LE2": {
+      "2034-2065": [
+        {
+          "doy": 1,
+          "doy_max": 2.52,
+          "doy_mean": 0.552,
+          "doy_min": 0.1,
+          "water_year_index": 93
+        },
+        {
+          "doy": 2,
+          "doy_max": 3.05,
+          "doy_mean": 0.558,
+          "doy_min": 0.1,
+          "water_year_index": 94
+        },
+        ...
+        {
+          "doy": 365,
+          "doy_max": 1.02,
+          "doy_mean": 0.224,
+          "doy_min": 0.1,
+          "water_year_index": 91
+        },
+        {
+          "doy": 366,
+          "doy_max": 0.16,
+          "doy_mean": 0.143,
+          "doy_min": 0.1,
+          "water_year_index": 92
+        }
+      ]
+    },
+    ...
+  },
+  "id": "81000004",
+  "latitude": 70.8292,
+  "longitude": -155.42,
+  "metadata": {
+    "source": {
+      "citation": "Dylan Blaskey, Keith Musselman, Andrew Newman, & Yifan Cheng. (2024). Alaskan river discharge, temperature, and climate data for a climate reference (1990-2021) and at mid-century (2034-2065). Arctic Data Center. doi:10.18739/A25M62870."
+    },
+    "variables": {
+      "doy": {
+        "description": "Day of year (1-366); all years are treated as leap years for consistency.",
+        "units": "day of year"
+      },
+      "doy_max": {
+        "description": "Maximum stream temperature on the specified day of year, aggregated over all years in the era.",
+        "units": "degC"
+      },
+      "doy_mean": {
+        "description": "Mean stream temperature on the specified day of year, aggregated over all years in the era.",
+        "units": "degC"
+      },
+      "doy_min": {
+        "description": "Minimum stream temperature on the specified day of year, aggregated over all years in the era.",
+        "units": "degC"
+      },
+      "water_year_index": {
+        "description": "Water year day index (1-366), where the water year starts on October 1 (DOY 275) and ends on September 30 (DOY 274).",
+        "units": "water year day index"
+      }
+    }
+  },
+  "watershed": "19060206",
+  "watershed_outlet": true
+}
+
+ +

+ Note that latitude and longitude values represent the approximate centroid of + the stream segment. Modeled data uses a 366 day year. The above output is + structured like this: +

+ +
+{
+  "data": {
+    <model>: {
+      <era>: [
+        {
+          <variable>: <value>,
+          ...
+        },
+        ...
+      ],
+      ...
+    },
+    ...
+  },
+  "id": <stream ID>,
+  "latitude": <latitude of stream segment>,
+  "longitude": <longitude of stream segment>,
+  "metadata": {
+    "source": {
+      "citation": <academic reference for source data>
+    },
+    "variables": {
+      <variable>: {
+        "description": <description of variable>,
+        "units": <units of variable>
+      },
+      ...
+    }
+  },
+  "watershed": <HUC8 or Yukon watershed ID>,
+  "watershed_outlet": <true if segment is a watershed outlet>
+}
+
+

Available Streamflow Statistics

@@ -703,6 +1013,324 @@

Available Streamflow Statistics

+

Available Water Temperature Statistics

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDDescriptionUnits
wt_days_gt13_meanMean annual count of days with stream temperature > 13°Cdays per year
wt_days_gt18_meanMean annual count of days with stream temperature > 18°Cdays per year
wt_days_gt20_meanMean annual count of days with stream temperature > 20°Cdays per year
wt_mean_janMean of monthly mean stream temperatures for Januarydegrees Celsius
wt_mean_febMean of monthly mean stream temperatures for Februarydegrees Celsius
wt_mean_marMean of monthly mean stream temperatures for Marchdegrees Celsius
wt_mean_aprMean of monthly mean stream temperatures for Aprildegrees Celsius
wt_mean_mayMean of monthly mean stream temperatures for Maydegrees Celsius
wt_mean_junMean of monthly mean stream temperatures for Junedegrees Celsius
wt_mean_julMean of monthly mean stream temperatures for Julydegrees Celsius
wt_mean_augMean of monthly mean stream temperatures for Augustdegrees Celsius
wt_mean_sepMean of monthly mean stream temperatures for Septemberdegrees Celsius
wt_mean_octMean of monthly mean stream temperatures for Octoberdegrees Celsius
wt_mean_novMean of monthly mean stream temperatures for Novemberdegrees Celsius
wt_mean_decMean of monthly mean stream temperatures for Decemberdegrees Celsius
wt_min_jan + Minimum monthly mean stream temperature for January across all years in + era + degrees Celsius
wt_min_feb + Minimum monthly mean stream temperature for February across all years in + era + degrees Celsius
wt_min_mar + Minimum monthly mean stream temperature for March across all years in + era + degrees Celsius
wt_min_apr + Minimum monthly mean stream temperature for April across all years in + era + degrees Celsius
wt_min_may + Minimum monthly mean stream temperature for May across all years in era + degrees Celsius
wt_min_jun + Minimum monthly mean stream temperature for June across all years in era + degrees Celsius
wt_min_jul + Minimum monthly mean stream temperature for July across all years in era + degrees Celsius
wt_min_aug + Minimum monthly mean stream temperature for August across all years in + era + degrees Celsius
wt_min_sep + Minimum monthly mean stream temperature for September across all years + in era + degrees Celsius
wt_min_oct + Minimum monthly mean stream temperature for October across all years in + era + degrees Celsius
wt_min_nov + Minimum monthly mean stream temperature for November across all years in + era + degrees Celsius
wt_min_dec + Minimum monthly mean stream temperature for December across all years in + era + degrees Celsius
wt_max_jan + Maximum monthly mean stream temperature for January across all years in + era + degrees Celsius
wt_max_feb + Maximum monthly mean stream temperature for February across all years in + era + degrees Celsius
wt_max_mar + Maximum monthly mean stream temperature for March across all years in + era + degrees Celsius
wt_max_apr + Maximum monthly mean stream temperature for April across all years in + era + degrees Celsius
wt_max_may + Maximum monthly mean stream temperature for May across all years in era + degrees Celsius
wt_max_jun + Maximum monthly mean stream temperature for June across all years in era + degrees Celsius
wt_max_jul + Maximum monthly mean stream temperature for July across all years in era + degrees Celsius
wt_max_aug + Maximum monthly mean stream temperature for August across all years in + era + degrees Celsius
wt_max_sep + Maximum monthly mean stream temperature for September across all years + in era + degrees Celsius
wt_max_oct + Maximum monthly mean stream temperature for October across all years in + era + degrees Celsius
wt_max_nov + Maximum monthly mean stream temperature for November across all years in + era + degrees Celsius
wt_max_dec + Maximum monthly mean stream temperature for December across all years in + era + degrees Celsius
wt_ann_max_temp_mean + Mean (over era years) of the annual maximum daily stream temperature + degrees Celsius
wt_ann_max_temp_doy_mean + Mean (over era years) Julian day of the annual maximum daily stream + temperature + Julian day
wt_7d_max_temp_mean + Mean (over era years) of the annual maximum 7-day rolling average stream + temperature + degrees Celsius
wt_7d_max_temp_doy_mean + Mean (over era years) Julian day of the center of the annual maximum + 7-day rolling average + Julian day
wt_cdd_may_sept_mean + Mean (over era years) annual cumulative degree days above 0°C, May + through September + degree-days Celsius
+

Source data

@@ -740,6 +1368,51 @@

Source data

>. + + + + + + + + + + + +
+ Rodgers K. B., S.-S. Lee, N. Rosenbloom, A. Timmermann, G. Danabasoglu, + C. Deser, J. Edwards, J.-E. Kim, I. R. Simpson, K. Stein, M. F. + Stuecker, R. Yamaguchi, T. Bódai, E.-S. Chung, L. Huang, W. M. + Kim, J.-F. Lamarque, D. L. Lombardozzi, W. R. Wieder, & S. G. Yeager + (2021). Ubiquity of human-induced changes in climate variability. Earth + System Dynamics. doi: + 10.5194/esd-12-1393-2021. +
+ Blaskey D., M. N. Gooseff, Y. Cheng, A. J. Newman, J. C. Koch, & K. N. + Musselman (2024). A high-resolution, daily hindcast (1990–2021) of + Alaskan river discharge and temperature from coupled and optimized + physical models. Water Resources Research. doi: + 10.1029/2023WR036217. +
+ Cheng Y., A. Craig, K. Musselman, A. Bennett, M. Seefeldt, J. Hamman, + & A. J. Newman (2024). Coupled high-resolution land-atmosphere modeling + for hydroclimate and terrestrial hydrology in Alaska and the Yukon River + Basin (1990–2021). Journal of Geophysical Research: Atmospheres. + doi: + 10.1029/2024JD041185. +
+ Cheng Y., A. Craig, K. Musselman, & A. Newman (2024). Multi-decadal + historical regional hydroclimate simulation with two mid 21st century + pseudo-global warming futures over Alaska and the Yukon at 4 km + resolution. NSF National Center for Atmospheric Research. doi: + 10.5065/ZPSB-PS82. +