Skip to content

Refactor rate-run orchestration with Prefect - #491

Draft
alxsmith wants to merge 34 commits into
mainfrom
refactor_run_orchestration
Draft

Refactor rate-run orchestration with Prefect#491
alxsmith wants to merge 34 commits into
mainfrom
refactor_run_orchestration

Conversation

@alxsmith

@alxsmith alxsmith commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Overview

Redesign the Prefect pipeline from scratch around a generic quartet-based orchestration model. The new pipeline replaces the old 1868-line run_pipeline.py with three focused modules (~1800 lines total) that scale cleanly across scenarios, utilities, and tariff structures.

Key changes:

  • pipeline_config.py — Centralized config: PipelineConfig, ScenarioConfig, RunDefaults dataclasses, YAML loader, deterministic naming functions for tariff stems, map stems, run names, and all derived paths.
  • run_pipeline.py — Prefect flows (run_batch → preflight → run_quartet → derive_tariffs), cairo_run task with subprocess isolation, threading.Semaphore concurrency gate, run index resume.
  • pipeline_derive.py — Structure handler registry (seasonal, flat, + future TOU) using a dispatch pattern. Adding a new tariff structure = one function + one dict entry.
  • run_scenario.py — Widened --run-num to accept string canonical names; writes a run index file on success for cross-invocation resume.
  • Pipeline YAML — Declarative per-utility config (pipeline_bge.yaml, pipeline_rie.yaml) that defines scenarios, quartet kinds, subclass configs, and all inputs in one place.

Reviewer focus

  • Naming convention correctness: trace a scenario through tariff_stem(), _scenario_map_stem(), _resolve_tariff_paths() and verify the derived paths match what CAIRO and downstream analysis expect.
  • Concurrency model: threading.Semaphore + ThreadPoolTaskRunner(max_workers=2) for delivery/supply parallelism within each stage.
  • Subprocess invocation: CAIRO runs as a child process for memory isolation; run index file is the resume/discovery mechanism.
  • derive_tariffs flow: explicit failure on missing allocation or RR values (no silent fallbacks).

Non-obvious implementation details

  • Run index over Prefect result cache: filesystem .runs/{name}.path files replace Prefect built-in caching. This gives cross-invocation resume without depending on Prefect server state.
  • Map stems exclude residual allocation: tariff maps partition buildings by subgroup, which is allocation-independent. Only tariff JSON stems encode both delivery and supply allocations.
  • tariff_base field: Required for single_rate quartets to decouple the tariff filename from the scenario name. Optional (unused) for multi-rate.
  • output_base in config: Batch output root is configurable rather than hardcoded, enabling local vs S3/FUSE paths.
  • Supply MC zeroing: For delivery-only runs, supply MC paths have /data.parquet replaced with /zero.parquet to zero out supply costs without branching logic in CAIRO.

alxsmith and others added 27 commits July 22, 2026 19:41
Enables the Prefect pipeline to pass output paths between tasks without S3 scans.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Replaces Justfile shell orchestration with precalc and calibrated tasks,
compact YAML config loading, and path derivation from tariff naming conventions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace _write_log (wrote to ~/rdp_run_logs/) with _log_provenance
which creates a Prefect markdown artifact visible in the UI per task run.
Also fix undefined outputs_base variable in derive_settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
Tags (batch, utility, scenario, commit) propagate to all tasks and are
filterable in the Prefect UI. Removes the per-task markdown artifact and
unused datetime import.

Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the scenario 'type' field with a single 'quartet' selector
(single_rate, multi_rate_collapsed, multi_rate_preserved) that derives the
per-stage rate modes and tariff_promotion policy. Unify the two evaluators
into one quartet_evaluator that always runs both stages; route the calibrated
stage through the promote_subgroup_tariff seam so it always consumes stage-1
*_calibrated.json outputs. resolve_subgroups now also generates the
calibrated-stage maps (keyed by _calibrated stems, which CAIRO matches to
tariff file stems). Add promote_subgroup for multi_rate_collapsed and update
the RIE/BGE pipeline YAMLs to the quartet field.

Co-authored-by: Cursor <cursoragent@cursor.com>
Update the Prefect pipeline context doc to the quartet-kind model: vocabulary,
the three-kind table, the tariff_promotion seam (promote_subgroup_tariff), the
unified quartet_evaluator running both stages, and calibrated-stage map
generation. Note the multi_rate_preserved RR caveat under Deferred.

Co-authored-by: Cursor <cursoragent@cursor.com>
Centralizes PipelineConfig/ScenarioConfig dataclasses, YAML loading,
and tariff stem derivation so quartets resolve paths from config
instead of hardcoded names.

Co-authored-by: Cursor <cursoragent@cursor.com>
Encode both allocations in the scenario name, add tariff_base and
promote, drop source/multi_rate, and use structure base for copy.

Co-authored-by: Cursor <cursoragent@cursor.com>
Match BGE: tariff_base, promote, allocation-encoded scenario name,
derived multi_rate path, and structure base instead of source.

Co-authored-by: Cursor <cursoragent@cursor.com>
generate_scenarios_yaml expands pipeline_bge.yaml into the per-run
YAML format that run_scenario.py consumes, keyed by canonical run
name. Replaces derive_settings and manual scenario YAML authoring.

Also adds validate_preflight_inputs for checking MC paths, ResStock,
RR YAMLs, gas maps, and single-rate tariff JSONs before any run.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
total_delivery_and_supply_rr = base_rr.get(
"total_delivery_and_supply_revenue_requirement"
)
customer_count = base_rr.get("test_year_customer_count")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is bad. flag for generalizing

"total_delivery_and_supply_revenue_requirement"
)
customer_count = base_rr.get("test_year_customer_count")
kwh_scale_factor = base_rr.get("resstock_kwh_scale_factor")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here. needs upstream cleaning

Co-authored-by: Cursor <cursoragent@cursor.com>
alxsmith and others added 6 commits July 28, 2026 21:42
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant