Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions .claude/pom_port_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,24 @@ would double-scale; only the test is worth adding).
VOM-normalization test, curtailment-incentive (#1614) test, services slack/feedforward/GroupReserve/
AGC/hydro variants, PowerModels-nonlinear & whitebox-reduction network testsets.

**Workstream C event framework:** 🟦 **descoped → IOM.** `EventModel`/`AbstractEventCondition`/
`FixedForcedOutage`-projection/template-integration is generic lifecycle+template machinery (same
layer as `DeviceModel`/`ServiceModel`/`ProblemTemplate`), POM already owns its share (event
parameter/constraint types + per-formulation `add_event_*!` hooks), and it isn't on PSI `main`.
`test_events.jl` is consequently blocked → not on this branch. Track in `iom_port_plan.md`.
**Workstream C event framework:** ✅ **build-time framework now in POM** (decision reversed: the
earlier "descope → IOM" was wrong — `EventModel{<:PSY.Contingency,…}`/`EventKey` are
PowerSystems-specific, so they belong in POM). Ported `EventModel` + the 4 `AbstractEventCondition`
types, `EventKey`, the real `add_event_arguments!`/`add_event_constraints!` builders, the
`EventParameter` parameter path, and offset→balance `add_to_expression!`. Storage hook: a generic
`events::Dict{AbstractEventKey,AbstractEventModel}` field + `get_events`/`set_event_model!` added to
**IOM**'s `DeviceModel` (mirrors `feedforwards`/`outages`); `get_max_active_power` IOM stub now
implemented in POM. New `test/test_events.jl` is **build/solve-level** (PSI's is Simulation-driven and
does not port): thermal/renewable/load × CopperPlate/DCP/ACP, plus a behavioral `AvailableStatusParameter→0`
forces-dispatch-to-zero test. **Simulation-time event evaluation** (`simulation_events.jl`,
`recorder_events.jl`) and PSI's `test_events.jl` remain out of scope (no Simulation in POM).
MBC tranche-count & concavity validation: ⏭️ already present (not blockers).

> Dev note: the `DeviceModel.events` field lives in IOM branch `ac/sienna1-port` (commit `d1e189a`,
> on top of the dual-mip-rounding commits). POM's IOM `[sources]` pin (both `Project.toml` and
> `test/Project.toml`) is set to `rev = "ac/sienna1-port"` until that IOM change merges to `main`;
> revert to `rev = "main"` then.

**Extra src fixes (beyond the PR list) to enable TransmissionInterface test coverage:**
- ✅ `core/problem_template.jl`: uncommented `_modify_device_model!` no-ops for
`ServiceModel{TransmissionInterface, ConstantMaxInterfaceFlow/VariableMaxInterfaceFlow}` (the
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ PowerFlows = "94fada2c-fd9a-4e89-8d82-81405f5cb4f6"
[sources]
InfrastructureSystems = {rev = "IS4", url = "https://github.com/Sienna-Platform/InfrastructureSystems.jl"}
PowerSystems = {rev = "psy6", url = "https://github.com/Sienna-Platform/PowerSystems.jl"}
InfrastructureOptimizationModels = {rev = "main", url = "https://github.com/Sienna-Platform/InfrastructureOptimizationModels.jl"}
InfrastructureOptimizationModels = {rev = "ac/sienna1-port", url = "https://github.com/Sienna-Platform/InfrastructureOptimizationModels.jl"}
PowerNetworkMatrices = {rev = "psy6", url = "https://github.com/Sienna-Platform/PowerNetworkMatrices.jl"}

[extensions]
Expand Down
37 changes: 36 additions & 1 deletion src/PowerOperationsModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,16 @@ import InfrastructureOptimizationModels:
start_up_cost,
_get_initial_condition_type,
set_ic_quantity!,
update_container_parameter_values!
update_container_parameter_values!,
# Event/contingency model storage (POM adds the convenience method that derives the key)
set_event_model!,
get_events,
# Outage-constraint RHS bound (IOM declares the stub, POM supplies the value)
get_max_active_power,
# Key/condition accessors POM extends for EventKey / EventModel conditions
get_entry_type,
get_component_type,
get_value

# Market bid cost: import IOM functions that POM extends with device-specific methods
import InfrastructureOptimizationModels:
Expand Down Expand Up @@ -235,6 +244,8 @@ include("core/expressions.jl")
include("core/constraints.jl")
include("core/auxiliary_variables.jl")
include("core/parameters.jl")
include("core/event_keys.jl")
include("core/event_model.jl")
include("core/formulations.jl")
include("core/bilinear_configs.jl")
include("core/network_formulations.jl")
Expand All @@ -251,6 +262,10 @@ include("common_models/add_to_expression.jl")
include("common_models/objective_function.jl")
# add_param_container.jl: moved into IOM
include("common_models/add_parameters.jl")
# Contingency-event builders (override the no-op event stubs in feedforward_interface.jl)
include("common_models/contingency.jl")
include("common_models/contingency_arguments.jl")
include("common_models/contingency_constraints.jl")
include("common_models/make_system_expressions.jl")
include("common_models/reserve_range_constraints.jl")
include("common_models/quadratic_converter_loss.jl")
Expand Down Expand Up @@ -391,6 +406,26 @@ export DeviceModel
export ServiceModel
export OptimizationContainer

# Event / contingency models
export EventModel
export EventKey
export AbstractEventCondition
export ContinuousCondition
export PresetTimeCondition
export StateVariableValueCondition
export DiscreteEventCondition
export set_event_model!
export get_events
# Event parameters
export AvailableStatusParameter
export AvailableStatusChangeCountdownParameter
export ActivePowerOffsetParameter
export ReactivePowerOffsetParameter
# Event constraints
export ActivePowerOutageConstraint
export ReactivePowerOutageConstraint
export ActivePowerPumpOutageConstraint

# Initial Conditions Quantities
export DevicePower
export DeviceStatus
Expand Down
65 changes: 65 additions & 0 deletions src/common_models/contingency.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Build-time contingency-event helpers: parameter defaults and the EventParameter
# parameter-container path. Ported from PowerSimulations.jl `src/contingency_model/`,
# adapted to POM's type-based accessor conventions and IOM-qualified container internals.

# The EventParameter outage constraint (IOM `range_constraint.jl`) bounds dispatch by
# `get_max_active_power(device) * AvailableStatusParameter`. POM supplies the value.
get_max_active_power(d::PSY.Device) = PSY.get_max_active_power(d, PSY.SU)

#! format: off
# These values could change depending on the event modeling choices.
get_parameter_multiplier(::EventParameter, ::PSY.Device, ::EventModel) = 1.0
get_initial_parameter_value(::ActivePowerOffsetParameter, ::PSY.Device, ::EventModel) = 0.0
get_initial_parameter_value(::ReactivePowerOffsetParameter, ::PSY.Device, ::EventModel) = 0.0
get_initial_parameter_value(::AvailableStatusChangeCountdownParameter, ::PSY.Device, ::EventModel) = 0.0
get_initial_parameter_value(::AvailableStatusParameter, ::PSY.Device, ::EventModel) = 1.0
#! format: on

function _add_parameters!(
container::OptimizationContainer,
::Type{T},
devices::Vector{U},
device_model::DeviceModel{U, W},
event_model::EventModel{V, X},
) where {
T <: EventParameter,
U <: PSY.Component,
V <: PSY.Contingency,
W <: AbstractDeviceFormulation,
X <: AbstractEventCondition,
}
@debug "adding" T U V _group = IOM.LOG_GROUP_OPTIMIZATION_CONTAINER

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Do we have similar debug statements elsewhere?

time_steps = get_time_steps(container)
names = PSY.get_name.(devices)
# The contingency type is build-time metadata only; the optimization container keys
# the parameter by the device type for both the `ParameterKey` and `affected_devices`.
parameter_container = add_param_container!(container, T, U, U, names, time_steps)

jump_model = get_jump_model(container)
parent_mult = IOM.get_multiplier_array_data(parameter_container)
parent_param = IOM.get_parameter_array_data(parameter_container)

for (i, d) in enumerate(devices)
ini_val = get_initial_parameter_value(T(), d, event_model)
IOM._set_multiplier_at!(
parent_mult,
get_parameter_multiplier(T(), d, event_model),
i,
)
for t in time_steps
IOM._set_parameter_at!(parent_param, jump_model, ini_val, i, t)
end
end
return
end

function add_parameters!(
container::OptimizationContainer,
::Type{T},
devices::Vector{U},
device_model::DeviceModel{U, W},
event_model::EventModel,
) where {T <: EventParameter, U <: PSY.Component, W <: AbstractDeviceFormulation}
_add_parameters!(container, T, devices, device_model, event_model)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Why do we have a helper if this is the call site?

return
end
Loading
Loading