Skip to content

eBFE 2D Breakout Geometry Preparation

Prepare a contained HUC12 breakout from Upper Guadalupe 3 plan 08. This example qualifies the parent, clones plan/geometry/unsteady associations, trims mesh-owned features, regenerates the cloned mesh, and reviews parent-face flux locations.

Boundary contract: existing boundary data are inventoried and the unsteady file is cloned byte-for-byte. This notebook does not author, relocate, reinterpret, or run any breakout boundary condition, and it never computes a hydraulic plan.

Engineering review sequence

  1. Confirm that plan 08 is a pure 2D, unsteady model and the HUC12 is contained.
  2. Review retained, clipped, dropped, and intentionally preserved geometry features.
  3. Optionally clone and remesh in an isolated working copy.
  4. Verify plan associations and the complete unsteady-file digest.
  5. Map candidate parent-face flux locations for future boundary-condition design.
  6. Record existing forcing and unresolved proposed breakout boundary locations as Next Steps.

The default committed state is read-only. Set RAS_COMMANDER_APPLY_BREAKOUT_GEOMETRY=1 only after providing a disposable project copy in RAS_COMMANDER_BREAKOUT_WORKING_PROJECT.

Python
from __future__ import annotations

import os
from pathlib import Path

import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import Markdown, display

from ras_commander import (
    Breakout2DSpec,
    RasBreakout2D,
    RasPrj,
    init_ras_project,
)

REPO_ROOT = Path.cwd() if (Path.cwd() / "examples").is_dir() else Path.cwd().parent
BOUNDARY_PATH = REPO_ROOT / "examples" / "data" / "956_huc12_121002010305.geojson"
SOURCE_PROJECT = Path(os.environ["RAS_COMMANDER_EBFE_UPGU3_PROJECT"])
WORKING_VALUE = os.getenv("RAS_COMMANDER_BREAKOUT_WORKING_PROJECT", "").strip()
WORKING_PROJECT = Path(WORKING_VALUE) if WORKING_VALUE else None
APPLY_GEOMETRY_PREPARATION = os.getenv(
    "RAS_COMMANDER_APPLY_BREAKOUT_GEOMETRY", "0"
).strip() == "1"
RAS_VERSION = os.getenv("RAS_COMMANDER_HECRAS_VERSION", "6.6")

assert SOURCE_PROJECT.is_dir(), SOURCE_PROJECT
assert BOUNDARY_PATH.is_file(), BOUNDARY_PATH
if APPLY_GEOMETRY_PREPARATION:
    assert WORKING_PROJECT is not None and WORKING_PROJECT.is_dir()
    assert SOURCE_PROJECT.resolve() != WORKING_PROJECT.resolve()

pd.set_option("display.max_rows", 100)
pd.set_option("display.max_colwidth", 100)
print(f"Source: {SOURCE_PROJECT}")
print(f"Geometry preparation enabled: {APPLY_GEOMETRY_PREPARATION}")
Text Only
Source: C:\Users\billk_clb\Documents\Codex\2026-08-28\one-of-the-things-that-we\model_workspaces\upgu3-face-flow-parent
Geometry preparation enabled: False
Python
source_ras = RasPrj()
init_ras_project(
    SOURCE_PROJECT,
    RAS_VERSION,
    ras_object=source_ras,
    load_results_summary=False,
    hide_intro=True,
)

spec = Breakout2DSpec(
    source_plan="08",
    source_2d_area="UPGU3_2DArea",
    child_boundary=BOUNDARY_PATH,
    breakout_id="HUC12-121002010305",
)
preflight = RasBreakout2D.preflight(spec, ras_object=source_ras)
assert preflight.is_ready, preflight.blocking_issues

summary = pd.DataFrame(
    [
        ("Source plan", "p08"),
        ("Geometry / unsteady", f"g{preflight.source_geometry_number} / u{preflight.source_unsteady_number}"),
        ("Plan classification", "unsteady_2d / pure 2D"),
        ("2D flow area", spec.source_2d_area),
        ("Base cell size", f"{preflight.base_cell_size:,.0f} ft"),
        ("Child area", f"{preflight.child_boundary.geometry.iloc[0].area / 27_878_400:,.2f} sq mi"),
        ("Blocking checks passed", f"{int(preflight.checks['passed'].sum())} / {len(preflight.checks)}"),
        ("Hydraulic computation", "NOT RUN"),
        ("Boundary authoring", "NOT PERFORMED"),
    ],
    columns=["review_item", "value"],
)
display(summary)
display(preflight.checks[["check_id", "passed", "blocking", "message"]])
review_item value
0 Source plan p08
1 Geometry / unsteady g01 / u01
2 Plan classification unsteady_2d / pure 2D
3 2D flow area UPGU3_2DArea
4 Base cell size 200 ft
5 Child area 24.73 sq mi
6 Blocking checks passed 6 / 6
7 Hydraulic computation NOT RUN
8 Boundary authoring NOT PERFORMED
check_id passed blocking message
0 pure_2d_plan True True Source plan must be classified as valid unsteady pure 2D
1 single_2d_flow_area True True Initial breakout preparation requires exactly one 2D flow area
2 unsupported_structures_absent True True Initial 2D breakout preparation does not support 1D or structure elements
3 child_within_parent True True Child boundary must be contained by the parent 2D area
4 boundary_partition_closes True True Inherited and artificial segments must close the child perimeter
5 reference_points_do_not_require_trim True True Reference points requiring trim are outside the initial implementation
Python
from shapely import concave_hull, contains_xy
from shapely.geometry import MultiPoint

from ras_commander import HdfMesh, HdfResultsMesh

parent_topology = HdfMesh.get_mesh_sloped_topology(
    preflight.source_geometry_hdf,
    spec.source_2d_area,
)
parent_max_ws = HdfResultsMesh.get_mesh_max_ws(preflight.source_plan_hdf)
parent_max_ws = (
    parent_max_ws[parent_max_ws["mesh_name"] == spec.source_2d_area]
    .set_index("cell_id")
    .reindex(np.arange(parent_topology["n_cells"]))
)

parent_cell_xy = parent_topology["cell_centers"]
parent_terrain = gpd.GeoDataFrame(
    {
        "cell_id": np.arange(parent_topology["n_cells"]),
        "minimum_terrain_elevation": parent_topology["cell_min_elev"],
        "maximum_water_surface": parent_max_ws["maximum_water_surface"].to_numpy(),
    },
    geometry=gpd.points_from_xy(parent_cell_xy[:, 0], parent_cell_xy[:, 1]),
    crs=preflight.parent_boundary.crs,
)
parent_terrain["maximum_depth"] = (
    parent_terrain["maximum_water_surface"]
    - parent_terrain["minimum_terrain_elevation"]
)
wet_mask = (
    np.isfinite(parent_terrain["maximum_depth"])
    & (parent_terrain["maximum_depth"] > 0.1)
)
wet_xy = parent_cell_xy[wet_mask.to_numpy()]
inundation_geometry = (
    concave_hull(MultiPoint(wet_xy), ratio=0.05, allow_holes=True)
    .buffer(preflight.base_cell_size * 0.75)
    .intersection(preflight.parent_boundary.geometry.iloc[0])
)
parent_inundation = gpd.GeoDataFrame(
    {"source": ["parent p08 maximum WSE; depth > 0.1 ft"]},
    geometry=[inundation_geometry],
    crs=preflight.parent_boundary.crs,
)
child_terrain_mask = contains_xy(
    preflight.child_boundary.geometry.iloc[0],
    parent_cell_xy[:, 0],
    parent_cell_xy[:, 1],
)

parent_context_summary = pd.DataFrame(
    [{
        "terrain_source": preflight.source_geometry_hdf.name,
        "terrain_metric": "minimum terrain elevation at parent mesh cells",
        "result_source": preflight.source_plan_hdf.name,
        "inundation_definition": "parent p08 maximum depth > 0.1 ft",
        "parent_cells": len(parent_terrain),
        "wet_cells": int(wet_mask.sum()),
        "wet_cell_fraction": float(wet_mask.mean()),
    }]
)
display(parent_context_summary)

fig, ax = plt.subplots(figsize=(12, 11))
terrain_artist = ax.hexbin(
    parent_cell_xy[:, 0],
    parent_cell_xy[:, 1],
    C=parent_terrain["minimum_terrain_elevation"],
    reduce_C_function=np.mean,
    gridsize=350,
    mincnt=1,
    cmap="terrain",
    linewidths=0,
    alpha=0.72,
)
preflight.parent_boundary.boundary.plot(
    ax=ax,
    color="#334155",
    linewidth=1.0,
    label="parent 2D perimeter",
)
parent_inundation.boundary.plot(
    ax=ax,
    color="#2563eb",
    linewidth=2.0,
    label="parent p08 max inundation boundary",
)
preflight.child_boundary.plot(
    ax=ax,
    facecolor="#22d3ee",
    edgecolor="#0e7490",
    alpha=0.3,
    linewidth=2.5,
)
colors = {"inherited": "#16a34a", "artificial_cut": "#f97316"}
for segment_type, frame in preflight.boundary_segments.groupby("segment_type"):
    frame.plot(
        ax=ax,
        color=colors[segment_type],
        linewidth=3.0,
        label=segment_type.replace("_", " ").title(),
    )
fig.colorbar(
    terrain_artist,
    ax=ax,
    fraction=0.035,
    pad=0.02,
    label="Parent terrain: minimum cell elevation (ft)",
)
ax.set_title(
    "Overall qualification with parent terrain and maximum inundation",
    loc="left",
    weight="bold",
)
ax.set_aspect("equal")
ax.legend(loc="upper right")
ax.set_axis_off()
plt.show()
Text Only
<ipython-input-1-b0d6732f2361>:119: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  plt.show()
terrain_source terrain_metric result_source inundation_definition parent_cells wet_cells wet_cell_fraction
0 UPGU3.g01.hdf minimum terrain elevation at parent mesh cells UPGU3.p08.hdf parent p08 maximum depth > 0.1 ft 295111 292677 0.991752

png

Python
action_counts = (
    preflight.feature_actions.groupby(["feature_type", "action"])
    .size()
    .rename("count")
    .reset_index()
)
display(action_counts)

fig, axes = plt.subplots(1, 2, figsize=(17, 8))
pivot = action_counts.pivot(index="feature_type", columns="action", values="count").fillna(0)
pivot.plot(kind="barh", stacked=True, ax=axes[0], color={"keep": "#16a34a", "clip": "#f59e0b", "drop": "#94a3b8", "replace": "#0ea5e9", "preserve": "#8b5cf6"})
axes[0].set_title("Feature disposition", loc="left", weight="bold")
axes[0].set_xlabel("feature count")
axes[0].set_ylabel("")

preflight.child_boundary.boundary.plot(ax=axes[1], color="#0e7490", linewidth=2.0)
style = {"drop": ("#94a3b8", 0.12, 0.4), "keep": ("#16a34a", 0.8, 1.0), "clip": ("#f59e0b", 0.9, 1.4), "preserve": ("#8b5cf6", 0.9, 2.0)}
for action, frame in preflight.feature_actions.groupby("action"):
    spatial = frame[frame.geometry.notna() & ~frame.geometry.is_empty]
    if spatial.empty or action not in style:
        continue
    color, alpha, width = style[action]
    spatial.plot(ax=axes[1], color=color, alpha=alpha, linewidth=width, label=action.title())
axes[1].set_title("Spatial feature decisions", loc="left", weight="bold")
axes[1].set_aspect("equal")
axes[1].set_axis_off()
axes[1].legend(loc="upper right")
plt.tight_layout()
plt.show()
Text Only
<ipython-input-1-f77311fc02f7>:29: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  plt.show()
feature_type action count
0 bc_line preserve 4
1 breakline clip 10
2 breakline drop 1534
3 breakline keep 132
4 mesh_area replace 1
5 reference_line clip 1
6 reference_line drop 13
7 reference_line keep 1
8 refinement_region drop 1
9 unsteady_boundary preserve 4

png

Existing boundary inputs - inventory only

These are the rows attached to the source plan's single unsteady file. They remain parent-model inputs; the geometry-only clone does not make them valid breakout boundaries. The complete cloned u## file must retain the same SHA-256 digest as the source.

Python
existing_boundaries = preflight.existing_boundaries.copy()
existing_boundaries.insert(0, "source_plan", "p08")
existing_boundaries.insert(1, "unsteady_file", preflight.source_unsteady_path.name)
display(existing_boundaries)

boundary_actions = preflight.feature_actions[
    preflight.feature_actions["feature_type"].isin(["bc_line", "unsteady_boundary"])
][["feature_type", "name", "action", "reason"]]
assert set(boundary_actions["action"]) == {"preserve"}
display(boundary_actions)
source_plan unsteady_file unsteady_number boundary_condition_number river_reach_name river_station storage_area_name pump_station_name area_2d bc_line_name bc_type hydrograph_type Interval Stage Hydrograph TW Check Flow Hydrograph Slope DSS File DSS Path Use DSS Use Fixed Start Time Fixed Start Date/Time Is Critical Boundary Critical Boundary Flow dss_part_a dss_part_b dss_part_c dss_part_d dss_part_e dss_part_f hydrograph_num_values Friction Slope friction_slope_value critical_fallback_flag full_path Flow Title Program Version Use Restart Precipitation Mode Wind Mode Met BC=Precipitation|Mode Met BC=Evapotranspiration|Mode Met BC=Precipitation|Expanded View Met BC=Precipitation|Constant Units Met BC=Precipitation|Gridded Source description
0 p08 UPGU3.u01 01 1 UPGU3_2DArea Inflow_from_UPGU2 Flow Hydrograph Flow Hydrograph 15MIN 0 0.0027 .\DSS Inputs\UPGU2.dss /REFERENCE LINES/UPGU2_2DArea: UPGU2_TransferLine/FLOW/01Dec2019-01Jan2020/30Minute/1pct/ True False , False REFERENCE LINES UPGU2_2DArea: UPGU2_TransferLine FLOW 01Dec2019-01Jan2020 30Minute 1pct 0 NaN NaN NaN C:\Users\billk_clb\Documents\Codex\2026-08-28\one-of-the-things-that-we\model_workspaces\upgu3-f... 1pct 6.31 0 Enable No Wind Forces Gridded None -1 mm/hr DSS
1 p08 UPGU3.u01 01 2 UPGU3_2DArea Outflow_to_UPGU4 Normal Depth None NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 0.0014,0 0.0014 0.0 C:\Users\billk_clb\Documents\Codex\2026-08-28\one-of-the-things-that-we\model_workspaces\upgu3-f... 1pct 6.31 0 Enable No Wind Forces Gridded None -1 mm/hr DSS
2 p08 UPGU3.u01 01 3 UPGU3_2DArea BC_1 Normal Depth None NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 0.001,0 0.0010 0.0 C:\Users\billk_clb\Documents\Codex\2026-08-28\one-of-the-things-that-we\model_workspaces\upgu3-f... 1pct 6.31 0 Enable No Wind Forces Gridded None -1 mm/hr DSS
3 p08 UPGU3.u01 01 4 UPGU3_2DArea BC_2 Normal Depth None NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 0 0.001,0 0.0010 0.0 C:\Users\billk_clb\Documents\Codex\2026-08-28\one-of-the-things-that-we\model_workspaces\upgu3-f... 1pct 6.31 0 Enable No Wind Forces Gridded None -1 mm/hr DSS
feature_type name action reason
0 bc_line Inflow_from_UPGU2 preserve boundary_condition_scope_deferred
1 bc_line BC_1 preserve boundary_condition_scope_deferred
2 bc_line Outflow_to_UPGU4 preserve boundary_condition_scope_deferred
3 bc_line BC_2 preserve boundary_condition_scope_deferred
1697 unsteady_boundary Inflow_from_UPGU2 preserve cloned_byte_identical_boundary_scope_deferred
1698 unsteady_boundary Outflow_to_UPGU4 preserve cloned_byte_identical_boundary_scope_deferred
1699 unsteady_boundary BC_1 preserve cloned_byte_identical_boundary_scope_deferred
1700 unsteady_boundary BC_2 preserve cloned_byte_identical_boundary_scope_deferred

Optional geometry-only preparation

The following cell is the complete mutation boundary. It clones geometry, unsteady flow, and plan associations in the isolated working project; registers the cloned geometry in RASMapper; replaces the cloned perimeter and mesh-owned features; imports the edited text into the cloned HDF; and regenerates the cloned mesh. It does not call RasCmdr.compute_plan or any other hydraulic solver entry point.

Python
clone = None
preparation = None
if APPLY_GEOMETRY_PREPARATION:
    working_ras = RasPrj()
    init_ras_project(
        WORKING_PROJECT,
        RAS_VERSION,
        ras_object=working_ras,
        load_results_summary=False,
        hide_intro=True,
    )
    clone = RasBreakout2D.clone_plan_components(
        preflight,
        ras_object=working_ras,
        plan_title="HUC12 breakout geometry prep",
        plan_short_id="HUC12_PREP",
        geometry_title="HUC12 breakout geometry",
    )
    preparation = RasBreakout2D.prepare_cloned_geometry(
        preflight,
        clone,
        ras_object=working_ras,
        refresh_hdf=True,
        remesh=True,
        compute_property_tables=False,
        timeout=1200,
    )
    assert clone.boundaries_unchanged
    assert preparation.boundaries_unchanged
    display(
        pd.DataFrame(
            [{
                "plan": f"p{clone.plan_number}",
                "geometry": f"g{clone.geometry_number}",
                "unsteady": f"u{clone.unsteady_number}",
                "unsteady_sha256": clone.cloned_unsteady_sha256,
                "mesh_ok": preparation.mesh_result.ok,
                "containment_ok": preparation.containment_result.ok,
                "hydraulic_compute": False,
            }]
        )
    )
else:
    display(Markdown(
        "**Read-only review mode.** Set the two working-project environment variables "
        "documented above to execute the clone/trim/remesh cell in a disposable copy."
    ))

Read-only review mode. Set the two working-project environment variables documented above to execute the clone/trim/remesh cell in a disposable copy.

Parent-face flux locations

This is a read-only review of the already-computed parent p08 result. Positive flow is oriented outward from the proposed child; adjacent significant faces with the same dominant direction are grouped only to make candidate locations legible. These zones are not boundary-condition records.

Python
flux = RasBreakout2D.review_parent_boundary_flux(
    preflight,
    minimum_peak_flow=100.0,
    minimum_volume_fraction=0.0001,
    gap_multiplier=3.0,
)

flux_summary = pd.DataFrame(
    [{
        "flow_source": flux.source,
        "units": flux.units or "cfs",
        "cut_faces": len(flux.faces),
        "significant_faces": int(flux.faces["significant"].sum()),
        "candidate_zones": len(flux.zones),
        "boundary_records_created": 0,
    }]
)
display(flux_summary)
display(flux.zones.drop(columns="geometry"))
flow_source units cut_faces significant_faces candidate_zones boundary_records_created
0 native_face_flow cfs 1051 51 22 0
zone_id dominant_direction face_count face_ids peak_flow peak_abs_flow absolute_volume_fraction arrow_dx arrow_dy
0 FLUX-001 inflow 1 [233421] -2721.547119 2721.547119 0.006661 0.000000 -1.000000
1 FLUX-002 outflow 1 [234671] 719.005493 719.005493 0.002386 -1.000000 0.000000
2 FLUX-003 inflow 4 [234677, 235744, 234642, 236914] -13488.601944 13488.601944 0.033551 0.316228 -0.948683
3 FLUX-004 outflow 1 [245674] 185.765167 185.765167 0.000559 -0.432456 -0.901655
4 FLUX-005 inflow 1 [246774] -147.977173 147.977173 0.000606 0.970603 -0.240685
5 FLUX-006 inflow 1 [246821] 191.552246 191.552246 0.000686 0.856138 0.516747
6 FLUX-007 outflow 6 [250001, 250002, 251061, 251062, 251063, 251064] 2623.394371 2623.394371 0.006990 -0.763592 -0.645699
7 FLUX-008 inflow 1 [251145] -1055.620850 1055.620850 0.004441 0.474791 -0.880098
8 FLUX-009 outflow 2 [251144, 609439] 1737.797363 1737.797363 0.005773 -0.879090 -0.476656
9 FLUX-010 inflow 4 [253416, 252230, 252228, 253394] -2730.677063 2730.677063 0.022034 0.954913 -0.296887
10 FLUX-011 outflow 5 [254640, 254638, 258298, 258299, 258300] 7559.067505 7559.067505 0.028360 -0.970143 -0.242536
11 FLUX-012 inflow 1 [260817] -416.468201 416.468201 0.003085 1.000000 0.000000
12 FLUX-013 outflow 3 [261971, 262032, 264292] 3433.065857 3433.065857 0.018166 -1.000000 0.000000
13 FLUX-014 outflow 2 [266743, 266744] 535.511383 535.511383 0.002757 -0.928703 -0.370823
14 FLUX-015 inflow 1 [608568] 101.165726 101.165726 0.000981 0.958237 -0.285976
15 FLUX-016 outflow 1 [270096] 206.395370 206.395370 0.001639 -0.973735 0.227686
16 FLUX-017 inflow 3 [270095, 270099, 270102] -2058.148224 2058.148224 0.016605 0.825201 -0.564839
17 FLUX-018 outflow 1 [272441] 12568.073242 12568.073242 0.066514 -0.936827 -0.349792
18 FLUX-019 inflow 3 [271276, 271277, 271278] -28641.406738 28641.406738 0.166411 0.611946 -0.790900
19 FLUX-020 outflow 2 [608572, 608573] 28659.317017 28659.317017 0.148783 -0.840215 -0.542254
20 FLUX-021 inflow 1 [608580] -19688.863281 19688.863281 0.116246 0.857168 -0.515036
21 FLUX-022 outflow 6 [271333, 272468, 271316, 270164, 270165, 270166] 40644.500977 40644.500977 0.296010 -0.273037 -0.962004
Python
fig, axes = plt.subplots(1, 2, figsize=(18, 9))


def draw_flux_review(ax, *, label_zones=False):
    child_terrain = parent_terrain.loc[child_terrain_mask]
    ax.hexbin(
        child_terrain.geometry.x,
        child_terrain.geometry.y,
        C=child_terrain["minimum_terrain_elevation"],
        reduce_C_function=np.mean,
        gridsize=160,
        mincnt=1,
        cmap="terrain",
        linewidths=0,
        alpha=0.58,
    )
    preflight.child_boundary.plot(
        ax=ax,
        facecolor="none",
        edgecolor="#0369a1",
        alpha=0.25,
        linewidth=2.0,
    )
    flux.faces[~flux.faces["significant"]].plot(
        ax=ax,
        color="#94a3b8",
        linewidth=0.6,
        alpha=0.25,
        label="reviewed face",
    )
    for direction, frame in flux.zones.groupby("dominant_direction"):
        color = "#dc2626" if direction == "outflow" else "#2563eb"
        frame.plot(
            ax=ax,
            color=color,
            linewidth=4.0,
            label=f"candidate {direction}",
        )
        for row in frame.itertuples(index=False):
            point = row.geometry.interpolate(0.5, normalized=True)
            scale = max(preflight.base_cell_size * 6.0, 800.0)
            ax.annotate(
                "",
                xy=(point.x + row.arrow_dx * scale, point.y + row.arrow_dy * scale),
                xytext=(point.x, point.y),
                arrowprops={"arrowstyle": "-|>", "color": color, "lw": 1.8},
            )
            if label_zones:
                ax.text(
                    point.x,
                    point.y,
                    f"{row.zone_id}\n{row.peak_abs_flow:,.0f} cfs",
                    fontsize=7,
                    color="#0f172a",
                    bbox={
                        "facecolor": "white",
                        "alpha": 0.8,
                        "edgecolor": "none",
                        "pad": 1.5,
                    },
                )
    ax.set_aspect("equal")
    ax.set_axis_off()


draw_flux_review(axes[0])
axes[0].set_title("Overall candidate locations", loc="left", weight="bold")
axes[0].legend(loc="upper right")

draw_flux_review(axes[1], label_zones=True)
zone_min_x, zone_min_y, zone_max_x, zone_max_y = flux.zones.total_bounds
zone_width = zone_max_x - zone_min_x
zone_height = zone_max_y - zone_min_y
zone_pad = max(preflight.base_cell_size * 8.0, max(zone_width, zone_height) * 0.12)
axes[1].set_xlim(zone_min_x - zone_pad, zone_max_x + zone_pad)
axes[1].set_ylim(zone_min_y - zone_pad, zone_max_y + zone_pad)
axes[1].set_title("Flux-zone detail with outward-flow arrows", loc="left", weight="bold")

fig.suptitle(
    "Candidate parent-face flux locations: review evidence only",
    x=0.01,
    ha="left",
    fontsize=15,
    weight="bold",
)
plt.tight_layout()
plt.show()

ordered = flux.zones.sort_values("peak_abs_flow")
fig, ax = plt.subplots(figsize=(11, max(5, len(ordered) * 0.3)))
bar_colors = ordered["dominant_direction"].map(
    {"inflow": "#2563eb", "outflow": "#dc2626"}
)
ax.barh(ordered["zone_id"], ordered["peak_abs_flow"], color=bar_colors)
ax.set_title("Candidate flux-zone peak magnitude", loc="left", weight="bold")
ax.set_xlabel("peak combined flow (cfs)")
ax.grid(axis="x", alpha=0.2)
plt.tight_layout()
plt.show()
Text Only
<ipython-input-1-541e277286ad>:87: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  plt.show()
<ipython-input-1-541e277286ad>:99: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  plt.show()

png

png

Next Steps - boundary conditions remain unresolved

The geometry workflow ends here. A separate reviewed workflow must decide which artificial-cut locations require forcing, select the correct hydraulic type, source and synchronize parent time series, offset/trim BC lines so adjacent records cannot bind the same face, and validate the breakout against the parent. Existing parent BCs are not automatically transferable merely because their unsteady rows were preserved.

Python
next_steps = flux.zones[[
    "zone_id", "dominant_direction", "face_count", "peak_abs_flow", "absolute_volume_fraction"
]].copy()
next_steps.insert(0, "proposed_breakout_location", True)
next_steps["proposed_bc_type"] = "TBD - future boundary-method workflow"
next_steps["time_series_source"] = "TBD - derive from parent p08 after method approval"
next_steps["applied_in_this_notebook"] = False

forcing_register = pd.DataFrame(
    [
        {"input": "Source unsteady boundary rows", "status": "Cloned byte-for-byte; not adapted to child", "future_decision": "Determine applicability/location"},
        {"input": "Rain-on-grid precipitation", "status": "Contained in cloned unsteady input", "future_decision": "Verify DSS path and forcing equivalence before any run"},
        {"input": "Candidate child cut-face zones", "status": f"{len(next_steps)} locations mapped; none authored", "future_decision": "Choose type and parent-derived time series"},
        {"input": "Hydraulic simulation", "status": "Not run", "future_decision": "Run only after independent BC review"},
    ]
)
display(forcing_register)
display(next_steps)
assert not next_steps["applied_in_this_notebook"].any()
input status future_decision
0 Source unsteady boundary rows Cloned byte-for-byte; not adapted to child Determine applicability/location
1 Rain-on-grid precipitation Contained in cloned unsteady input Verify DSS path and forcing equivalence before any run
2 Candidate child cut-face zones 22 locations mapped; none authored Choose type and parent-derived time series
3 Hydraulic simulation Not run Run only after independent BC review
proposed_breakout_location zone_id dominant_direction face_count peak_abs_flow absolute_volume_fraction proposed_bc_type time_series_source applied_in_this_notebook
0 True FLUX-001 inflow 1 2721.547119 0.006661 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
1 True FLUX-002 outflow 1 719.005493 0.002386 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
2 True FLUX-003 inflow 4 13488.601944 0.033551 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
3 True FLUX-004 outflow 1 185.765167 0.000559 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
4 True FLUX-005 inflow 1 147.977173 0.000606 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
5 True FLUX-006 inflow 1 191.552246 0.000686 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
6 True FLUX-007 outflow 6 2623.394371 0.006990 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
7 True FLUX-008 inflow 1 1055.620850 0.004441 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
8 True FLUX-009 outflow 2 1737.797363 0.005773 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
9 True FLUX-010 inflow 4 2730.677063 0.022034 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
10 True FLUX-011 outflow 5 7559.067505 0.028360 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
11 True FLUX-012 inflow 1 416.468201 0.003085 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
12 True FLUX-013 outflow 3 3433.065857 0.018166 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
13 True FLUX-014 outflow 2 535.511383 0.002757 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
14 True FLUX-015 inflow 1 101.165726 0.000981 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
15 True FLUX-016 outflow 1 206.395370 0.001639 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
16 True FLUX-017 inflow 3 2058.148224 0.016605 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
17 True FLUX-018 outflow 1 12568.073242 0.066514 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
18 True FLUX-019 inflow 3 28641.406738 0.166411 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
19 True FLUX-020 outflow 2 28659.317017 0.148783 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
20 True FLUX-021 inflow 1 19688.863281 0.116246 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False
21 True FLUX-022 outflow 6 40644.500977 0.296010 TBD - future boundary-method workflow TBD - derive from parent p08 after method approval False