Sienna-Platform / Sienna-Platform/PowerOperationsModels.jl
Merge the device and service `_add_time_series_parameters!` bodies
Nobody has claimed this yet.
- 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 inb0c5d86, for branches in5e2e802. - Missing
resolutionon the UUID lookup. Withintervalalone, 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
VariableReservewith 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 forServiceModel- a generic
_get_time_series_name(::Type{T}, ::PSY.Component, ::ServiceModel)fallback; the existing generic one at:394is bound toDeviceModel, and the onlyServiceModelmethod (:403) is for the ORDC piecewise parameters, which areObjectiveFunctionParameterand never reach this path - a no-op
_check_branch_rating_tsforServiceModel
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.jlandtest_mbc_parameter_population.jlshould pass untouched. The two regression tests added inb0c5d86and5e2e802pin the dedup and resolution behavior on both sides.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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