Skip to main content

run-hy8

run hy8 and parse outputs via python

Utilities for assembling HY-8 project files and running the HY-8 executable from Python. The focus is on a pythonic, extensible object model that can be imported into other scripts, with Windows/Python 3.13 and 3.14 as the supported runtimes.

Highlights

  • Strongly typed dataclasses that describe flows, tailwater, roadway geometry, and culvert barrels.
  • Separation between domain objects (run_hy8.models), on-disk serialization (run_hy8.writer), and process orchestration (run_hy8.executor).
  • Friendly validation helpers that raise actionable errors before the HY-8 binary is invoked. At the moment this library intentionally supports a subset of HY-8 features (constant tailwater elevation, paved/gravel/user roadway surfaces, and circular, box, and catalogue-based elliptical culverts). When a configuration requires more advanced HY-8 options we surface a clear error that instructs the caller to finish the edit in the HY-8 GUI.
  • Small CLI scaffold (python -m run_hy8) that can emit a demo file or build projects from JSON configs, and a validate-only mode to lint configs without writing any files.

Project Structure

The run-hy8 project is organized into the following modules:

  • run_hy8.models: Contains the core data classes that represent the HY-8 project structure, including Hy8Project, CulvertCrossing, CulvertBarrel, FlowDefinition, TailwaterDefinition, and RoadwayProfile.
  • run_hy8.reader: Handles the parsing of existing HY-8 project files (.hy8) into the object model.
  • run_hy8.writer: Serializes the object model back into HY-8 project files.
  • run_hy8.executor: Provides a wrapper around the HY-8 command-line executable for running simulations.
  • run_hy8.hydraulics: Contains helper functions for running hydraulic scenarios and parsing the results.
  • run_hy8.results: Parses the HY-8 output files (.rst and .rsql) into a more usable format.
  • run_hy8.cli: Implements the command-line interface for the run-hy8 package.
  • run_hy8.config: Handles the loading of project configurations from JSON files.
  • run_hy8.classes_references: Contains core data classes and references for run-hy8.
  • run_hy8.type_helpers: Contains enums and enum helpers shared between HY-8 domain models.
  • run_hy8.units: Contains unit conversion helpers shared across the run-hy8 domain.

Quick Start

python -m pip install --user -e .
from pathlib import Path
from run_hy8 import (
    Hy8Project,
    CulvertCrossing,
    CulvertBarrel,
    FlowDefinition,
    Hy8FileWriter,
)

project = Hy8Project(title="Sample project", designer="River Engineer")
crossing = CulvertCrossing(name="Crossing 1")
crossing.flow = FlowDefinition(minimum=10.0, design=25.0, maximum=50.0)
crossing.tailwater.constant_elevation = 99.1
crossing.tailwater.invert_elevation = 99.0
crossing.culverts.append(CulvertBarrel(name="Barrel 1", span=1.2, rise=1.2))
project.crossings.append(crossing)

writer = Hy8FileWriter(project)
hy8_file = writer.write(Path("output/sample.hy8"))
print(f"Wrote {hy8_file}")

CulvertBarrel defaults to a circular corrugated-steel pipe with a thin-edge-projecting inlet. For other shape/material combinations, select a semantic HY-8 version 8 inlet configuration explicitly:

from run_hy8 import ConcreteBoxInlet, CulvertBarrel, CulvertMaterial, CulvertShape

box = CulvertBarrel(
    name="Box 1",
    shape=CulvertShape.BOX,
    material=CulvertMaterial.CONCRETE,
    span=2.4,
    rise=1.2,
    inlet_configuration=ConcreteBoxInlet.SQUARE_EDGE_30_TO_75_DEG_WINGWALL,
)

Elliptical culverts use HY-8's version-pinned, material-specific size catalogues rather than arbitrary mathematical ellipses. HY-8 8.0.1.2 provides 23 concrete sizes and 40 Steel-or-Aluminum sizes. A requested span/rise pair must match an entry for the selected material; unsupported or reversed dimensions fail closed and are never replaced with a nearby size.

from run_hy8 import (
    CulvertBarrel,
    CulvertMaterial,
    CulvertShape,
    EllipticalConcreteInlet,
    EllipticalSteelOrAluminumInlet,
    find_concrete_ellipse_catalogue_size,
    find_steel_or_aluminum_ellipse_catalogue_size,
)

concrete_size = find_concrete_ellipse_catalogue_size(1.524, 0.9652)
concrete_ellipse = CulvertBarrel(
    name="Concrete ellipse",
    shape=CulvertShape.ELLIPTICAL,
    material=CulvertMaterial.CONCRETE,
    span=concrete_size.span_m,
    rise=concrete_size.rise_m,
    outlet_invert_station=20.0,
    inlet_configuration=EllipticalConcreteInlet.SQUARE_EDGE_WITH_HEADWALL,
)

steel_size = find_steel_or_aluminum_ellipse_catalogue_size(
    241.0 * 0.0254,
    156.0 * 0.0254,
)
steel_ellipse = CulvertBarrel(
    name="Steel/aluminum ellipse",
    shape=CulvertShape.ELLIPTICAL,
    material=CulvertMaterial.STEEL_OR_ALUMINUM,
    span=steel_size.span_m,
    rise=steel_size.rise_m,
    outlet_invert_station=20.0,
    inlet_configuration=EllipticalSteelOrAluminumInlet.HEADWALL,
)

Catalogue selection supplies HY-8's required BARRELGEOMETRY parameters and the size-specific Manning value. Steel-or-Aluminum catalogue roughness is not constant across all 40 sizes, so the selected catalogue row is authoritative when no explicit override is supplied.

Inlet configurations are separated by HY-8 shape and material: CircularConcreteInlet, CircularCorrugatedSteelInlet, CircularHdpeInlet, ConcreteBoxInlet, EllipticalConcreteInlet, and EllipticalSteelOrAluminumInlet. The package supports HY-8 version 8 project files only. The older context-free InletEdgeType and InletEdgeType71 inputs are deprecated and emit a LegacyInletConfigurationWarning when translated.

The empirical file-format findings and extension guidance are recorded in docs/hy8_v8_inlet_configurations.md.

Once an .hy8 file exists you can run HY-8 with run_hy8.executor.Hy8Executable. Each high-level action returns a CompletedProcess so scripting layers can inspect stdout/stderr or retry with different parameters.

TUFLOW integration ownership

TUFLOW-specific culvert ingestion and batch evaluation live in ryan-tools, not in this package.

The former scripts/culvert_demo-from-tuflow.py and scripts/culvert_demo-from-1d_nwk.py workflows have been migrated downstream in ryan-tools PR #100, where they support explicit hy8 or ryan-culverts engine selection. run-hy8 remains responsible for generic HY-8 project construction, execution and result parsing.

Reading existing HY-8 projects

Existing HY-8 files can be parsed back into the same object model via run_hy8.reader.load_project_from_hy8. The scripts/gen-hy8-example.py helper demonstrates the flow in a standalone script; inline usage looks like:

from pathlib import Path
from run_hy8 import Hy8FileWriter, load_project_from_hy8

source_path = Path("tests/example_crossings.hy8")
project = load_project_from_hy8(source_path)
project.title = "Round-tripped project"

round_tripped = Hy8FileWriter(project).write(Path("output/round_trip.hy8"))
print(f"Serialized {round_tripped}")

This is the same pipeline used in tests/test_reader.py to guarantee that we can faithfully read and re-write reference projects supplied by HY-8.

Parsing HY-8 outputs

When HY-8 finishes a run it emits .rst (culvert summary table) and .rsql (flow profile) files next to the project. run_hy8.results contains parsers for both formats plus a Hy8Results helper that merges them into easier-to-query rows:

from pathlib import Path
from run_hy8 import Hy8Results, parse_rst, parse_rsql

crossing = "Crossing 1"
rst_data = parse_rst(Path("output/sample.rst"))
rsql_profiles = parse_rsql(Path("output/sample.rsql"))

series = rst_data.get(crossing, {})
profiles = rsql_profiles.get(crossing, [])
results = Hy8Results(series, profiles)
best_design = results.nearest(target=50.0)
print(f"Design headwater: {best_design.headwater_elevation}")

Each result row also retains diagnostics for every culvert in the crossing. The culvert index is the zero-based value written by HY-8:

for culvert in best_design.culverts:
    print(
        culvert.name,
        culvert.inlet_control_depth,
        culvert.outlet_control_depth,
        culvert.full_length,
        culvert.free_length,
    )

HY-8 occasionally appends * or ** to a reported control depth. The numeric portion remains available as a float and the marker is retained separately in inlet_control_depth_qualifier or outlet_control_depth_qualifier. Numeric values remain in the unit system used by the HY-8 report. See the HY-8 control-depth qualifier legend for the meaning and limitations of * and **.

These utilities power scripts/batch_hy8_compare.py and let you automate regression checks without opening the HY-8 GUI.

HY-8 executable location

The repository stores its default HY-8 path inside HY8_PATH.txt. Update that file if HY-8 is installed somewhere else—the helpers automatically pick up the new value. Hy8Executable() falls back to the path recorded in the file (or the HY8_EXE / HY8_EXECUTABLE environment variables) so scripts can simply do Hy8Executable() without passing a path every time. Call Hy8Executable.configure_default_path(Path(...)) to override the default for the current process, or Hy8Executable.persist_default_path(Path(...)) to update both the in-memory default and the HY8_PATH.txt file. All command-line tooling also respects these settings; pass --run-exe to override on a per-invocation basis if needed.

Configuration via JSON

Intentional roadway studies can select RoadwayOvertoppingPolicy.WARN or ALLOW in the hydraulic helpers, or --roadway-overtopping warn|allow with CLI execution. The default ERROR checks actual reported roadway discharge after execution. See roadway coefficient, profile, policy and validation contracts.

For quick scripting, describe your project in JSON and let the CLI write the .hy8 file. The same schema is used by the checked-in sample_project.json:

{
  "project": {
    "title": "Culvert Replacement",
    "designer": "River Engineer",
    "units": "EN"
  },
  "crossings": [
    {
      "name": "Crossing A",
      "flow": {
        "minimum": 10,
        "design": 25,
        "maximum": 40
      },
      "tailwater": {
        "constant_elevation": 100.5,
        "invert_elevation": 99.0
      },
      "roadway": {
        "shape": "IRREGULAR",
        "width": 40,
        "surface": "paved",
        "stations": [-20, 0, 20],
        "elevations": [102, 101.5, 102]
      },
      "culverts": [
        {
          "name": "Barrel 1",
          "shape": "circle",
          "material": "concrete",
          "inlet_configuration": "square-edge-with-headwall",
          "span": 4.0,
          "rise": 4.0
        }
      ]
    }
  ]
}
python -m run_hy8 build --config project.json --output output/sample.hy8 --overwrite

If you pass --run-exe path\to\HY864.exe the CLI will immediately call -OpenRunSave after writing the project.

To lint a configuration without touching the filesystem:

python -m run_hy8 build --config sample_project.json --output ignored.hy8 --validate-only

Tests

Targeted unit tests live under tests. Run them with:

python -m pytest tests

Packaging & automation

For a normal installation from PyPI:

python -m pip install --upgrade run-hy8

For local development, install the source checkout with development dependencies:

python -m pip install -e ".[dev]"

Local builds use build_package.bat or python scripts/build_library.py, with wheel verification described in verified package builds. run_tests.bat runs pytest and accepts additional pytest arguments.

Eligible, successfully validated merges to main are published through the automated PyPI workflow. The release process uses a tag-scoped verified wheel rather than committing distribution artifacts.

License

This project is licensed under the Sustainable Use License v1.0 (SUL-1.0).

The software may be used and modified for personal, non-commercial, and internal business purposes, including commercial professional and consulting work where the software itself is not provided as a commercial product or service.

Commercial distribution, incorporation into software supplied commercially to third parties, or provision of the software or its functionality as a paid hosted service, SaaS, or API is not permitted under the free licence. See the LICENSE file for the governing terms.

Contributing

Contributions are welcome! If you would like to contribute to the project, please follow the guidelines in docs/agents.md. It is recommended to first open an issue to discuss any planned changes.

Before submitting a pull request, please ensure your changes are pyright-clean and that all tests pass. To get started with the development environment:

pip install -e .[dev]

Development Notes

Coding agents and contributors should follow the conventions in docs/agents.md, including the project-wide expectation for explicit type hints and pyright-clean changes.

Current Limitations

  • Constant tailwater elevation only: other HY-8 tailwater definitions are flagged so the user can finish in the GUI.
  • Intentional roadway overtopping is supported for constant and irregular roadway profiles. The high-level hydraulic helpers default to RoadwayOvertoppingPolicy.ERROR, which rejects actual parsed roadway discharge unless the caller selects WARN or ALLOW. Tailwater above a roadway crest alone does not imply actual flow.
  • The exact HY-8 submergence law for a user-defined roadway discharge coefficient has not been independently reconstructed. The adapter has been compared against HY-8, not independently calibrated as a hydraulic solver.
  • Elliptical geometry is limited to version-pinned HY-8 catalogued sizes for supported materials.
  • The JSON loader supports fundamental flows, roadway geometry and culvert barrels; more specialised HY-8 features such as rating curves and irregular channels remain deferred.

Legacy hy8runner

A separate hy8runner implementation lives under tests/hy8runner so we can use it for regression comparisons. It is not published or installed; the supported entry points are the automated regression test (tests/test_legacy_regression.py) and the comparison workflow in scripts/sample_crossing_compare.py, both of which import it via from .hy8runner.hy8_runner import Hy8Runner.

Metadata

Release files for run-hy8 2026.10.18.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for run-hy8 2026.10.18.2
File Interpreter ABI Platform
run_hy8-2026.10.18.2-py3-none-any.whl Python 3 none any Details

Release files / run_hy8-2026.10.18.2-py3-none-any.whl

Download URL run_hy8-2026.10.18.2-py3-none-any.whl
Size 76.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b31cafe37dd3bd05d14cba4708082abd1da643aa51202662bbfda7c495fb1c63
BLAKE2b-256 checksum
How to use checksums
5593d4f07d22bb9794b03c5189517dfb5e37f51844ce84ad802d5825fa96b28b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2026.10.18.2 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page