Sienna-Platform / Sienna-Platform/PowerOperationsModels.jl

Merge the device and service `_add_time_series_parameters!` bodies

Open
#226 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Julia
Stars
2
Forks
1
Avg merge
1d 16h
Merged PRs (30d)
26

Description

Sibling of #172 (construct_device!), #195 (network-family constructor pairs) and #223 (construct_service!), for the time series parameter path.

Raised by @luke-kiernan reviewing #206:

AI is flagging this as pretty close to the device version, add_parameters.jl:145. Combine?
There's some differences--behavior when TS is missing, value unwrapping, resolution handling--so it's not a no-brainer.

Deferred out of #206, which had already grown well past its original scope.

Why this matters more than tidiness

There are three copies of one algorithm, not two:

Method File:line Covers
_add_time_series_parameters! common_models/add_parameters.jl:146 devices
_add_time_series_parameters! common_models/add_parameters.jl:260 branches, network-reduction aware
_add_parameters! common_models/add_parameters.jl:801 services

The same pair of bugs was found and fixed independently in two of them during #206:

  • Undeduplicated UUID parameter axis. The axis is keyed by time series UUID, but each copy built one entry per component. add_time_series!(sys, [c1, c2], forecast) stores one array for many components, so both resolve to the same UUID and JuMP rejects the repeated axis element - a hard build failure. Fixed for services in b0c5d86, for branches in 5e2e802.
  • Missing resolution on the UUID lookup. With interval alone, a series stored at a resolution other than the model's is silently accepted and then read at that resolution. Fixed in the same two commits.

The device copy (:146) had both right all along. Three copies means the next fix has to land three times, and the evidence says it won't.

Current state of the delta (post-#206)

After b0c5d86 the device and service copies are close to identical. What remains:

Aspect Device :146 Service :801 To merge
Missing time series has_time_series guard, @debug + skip; error only if no component has it no guard, throws on the first one dispatch hook, see below
unwrap_for_param + @assert applied absent adopt the device version
Time series name _get_time_series_name(T, first(devices), model) hook get_time_series_names(model)[T] adopt the hook, needs a ServiceModel fallback
_check_branch_rating_ts called absent loosen the signature or add a no-op ServiceModel method
@debug "adding" trace present absent adopt
Component type argument D U eltype, mechanical
Formulation for the multiplier W from DeviceModel{D, W} V from ServiceModel{U, V} small accessor
Input type Vector or IS.FlattenIteratorWrapper Vector only widen

Everything below the first row is mechanical or a few lines.

The missing-time-series policy is not a disagreement to settle

It is tempting to read the two policies as a bug on one side. They are not answering the same question:

  • Devices: the parameter is opt-in per component. One DeviceModel{ThermalStandard, …} covers the whole fleet and only some units carry, say, a time-varying startup cost. Partial coverage is normal - that is why the guard exists. Making it loud would reject valid systems.
  • Services: the parameter is mandatory for the type. A VariableReserve with no "requirement" forecast is malformed, so throwing is correct.

So the policy has to survive as a dispatch point. This is not "pick one and unify", it is "unify the algorithm, keep the policy as a hook" - the same _maybe_* shape proposed in #223.

Note the device-side skip is the silent-failure pattern called out in .claude/CLAUDE.md. Worth revisiting on its own merits, but as a separate decision, not as a side effect of this merge.

Proposed direction

Merge :146 and :801 into one body plus a handful of small dispatching helpers:

  • _missing_ts_policy(model) - skip vs. throw
  • _formulation_type(::DeviceModel{D, W}) where {D, W} = W, likewise for ServiceModel
  • a generic _get_time_series_name(::Type{T}, ::PSY.Component, ::ServiceModel) fallback; the existing generic one at :394 is bound to DeviceModel, and the only ServiceModel method (:403) is for the ORDC piecewise parameters, which are ObjectiveFunctionParameter and never reach this path
  • a no-op _check_branch_rating_ts for ServiceModel

Keep the helpers reading type parameters so inference is preserved (_formulation_type above is inferable). Coordinate with #216, which is already working on inference in service construction.

Leave the branch copy alone

Folding :260 into the shared body is probably not worth it. It carries network reduction, branch-pair keying, its own axis builder (get_branch_argument_parameter_axes), a reduced-branch tracker for parallel arcs, and a different empty-set policy (@info and return rather than error). The hooks needed to absorb it would cost more than the duplication saves. It should stay structurally separate and simply be kept in sync.

Constraints

  • No behavior change for either path beyond the policy hook preserving today's semantics on both sides.
  • Run Test.detect_ambiguities; collapsing methods onto shared abstract bounds is how ambiguities appear.
  • test_services_constructor.jl, test_network_constructors_with_branch_rating_time_series.jl and test_mbc_parameter_population.jl should pass untouched. The two regression tests added in b0c5d86 and 5e2e802 pin the dedup and resolution behavior on both sides.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in common_models/add_parameters.jl at the device and service _add_time_series_parameters! bodies around lines 146 and 801, then read the related helpers and the inference work in #216. Compare the behavior against test_services_constructor.jl, test_network_constructors_with_branch_rating_time_series.jl, test_mbc_parameter_population.jl, and the regression tests from b0c5d86 and 5e2e802; done means shared behavior without changing either policy and a passing Test.detect_ambiguities run.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia
Domain
backend
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.