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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions