Skip to content

Translator timeslice implementation notes (decisions from staging PR #121) #123

Description

@nick-gorman

Context

PR #117 (new-format-translator) is the omnibus/baseline implementation of the new-format pipeline, including the timeslice work. It is being brought onto main as a series of small, separately-reviewable PRs rather than merged wholesale.

PR #121 stages the templater half (decoding AEMO's PLEXOS calendar into one window pattern per reference year). While compiling it, we made/validated several decisions that belong to the translator half and so land in later staging PRs. This issue records them so they aren't lost between PRs.

Established along the way: within each region and reference year the shipped v7.5 windows form an exact partition of the year (every day covered by exactly one timeslice — verified across all 75 region×reference-year groups). winter_reference is already the contiguous complement of peak+summer (a fixed 04-01 → 11-01 block for every reference year), with the weather variation carried by the peak/summer windows. The cycle boundary is 1 November; consecutive reference years abut there. The downstream "gap fallback" is therefore defensive code the real data never exercises. The items below lean on that structure holding.

Enforcement, as of #121

  • Tiling is enforced inline: _template_timeslices calls _raise_unless_windows_tile_the_year before returning, so a future calendar that fails to tile fails at template time. The check is boundary-based (sorted by start, each window must end where the next begins, the last wrapping to the first) — no day counting, leap-safe.
  • Two structural assumptions underpin the reference-year attribution, and are checked by standalone guards, not inline: _raise_unless_only_winter_crosses_financial_year (only winter may span 1 July, so weather-varying timeslices stay within one reference year) and _raise_unless_winter_is_constant_per_region (the one boundary-crossing timeslice is identical across reference years, so its attribution can't blur). These run on the real calendar inside test_shipped_calendar_decodes, so they're enforced per workbook version rather than on every decode — deliberately, to keep the focused decoder tests on minimal data.
  • The timeslices schema custom_validation rules (no-overlap, full-coverage) remain inert until the schema-validation layer is built; the guards above are the active enforcement until then.

To action in later 117 staging PRs

  1. Re-check coverage/non-overlap after pattern expansion. The templater's check is leap-safe (it verifies the windows' month-day boundaries chain, without counting days), and the templater deliberately preserves 02-29 (clamping is the translator's job — covered by test_convert_windows_to_month_days_preserves_leap_day). The translator re-expands patterns into concrete model years where a 02-29 boundary must be clamped to 02-28 in non-leap years. This is concrete, not hypothetical: reference year 2024 has a 29 February hot day (NSW and VIC) in the shipped calendar, so the clamp fires whenever the configured cycle places 2024 in a non-leap model year. Re-assert the partition on the expanded snapshot mapping, where that clamp could open a one-day seam.

  2. Add a round-trip test: decode → re-expand → original calendar. Decoding timeslice_RefYear5000.csv to per-reference-year patterns and re-expanding with the original reference_year_sequence reproduces the kept windows exactly (verified on v7.5: 1460/1460 windows match, start and end). This is the strongest correctness check on the decode — it catches mis-paired windows, reference-year mis-attribution, and any non-invertible decode, none of which the consistency guard catches (AEMO's cycle order is fixed, so a same-reference-year contamination would be identical across occurrences and pass consistency silently). It belongs in the translator PR because it needs the translator's _place_pattern_in_financial_year — run it against the real re-expansion, not a test-local copy.

  3. Use max per-direction capacity as link p_nom, not winter_reference. Removes the str.endswith(\"_winter_reference\") special-case in _extract_static_limits and keeps p_max_pu <= 1. Verify winter is the max for every path: if so this is a no-op rename; if not, the current code already emits p_max_pu > 1 for that path (a latent over-crediting bug this fixes).

  4. Remove the now-dead gap fallback in _build_link_pu_overrides. Once coverage is enforced and guaranteed, every snapshot is set by exactly one timeslice — drop the static-default seed (the 1.0 / static p_min_pu init) and instead assert the series is fully covered. Constraints need no change: the partition makes the additive "one instance per timeslice, scoped to its snapshots" model exactly-once-per-snapshot already.

  5. (Optional) Forbid mixing static (NaN) and timeslice-tagged rows for one entity in the network_transmission_path_limits and custom_constraints_rhs schemas. Each entity is either fully static or fully timeslice-resolved — never a partial mix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions