Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
0bf1ff5
Bilinear hydro formulation and IOM/PSY API updates
Apr 29, 2026
001b82a
Merge branch 'main' into ac/hydro-bilinear
acostarelli Apr 29, 2026
4ad672f
claude initial
Apr 30, 2026
b36ad3c
fix time-series parameter accessing; fix onparameter cost adding
May 4, 2026
e0d67dc
simplify constructor
May 5, 2026
e2e7871
formatting
May 5, 2026
8d93d17
address reviews: add type untyped arguments; support reservation, reg…
May 7, 2026
a3d2c6b
format
May 7, 2026
6b827d4
refactor: unify two-sided hybrid+storage methods via trait dispatch
May 8, 2026
59c994b
refactor: defer hybrid thermal range constraints to IOM helper
May 8, 2026
5dea6aa
Revert "refactor: defer hybrid thermal range constraints to IOM helper"
May 8, 2026
379d8c5
docs: rename Bounds → Domain in HybridDispatchWithReserves docstring
May 8, 2026
353ca8e
refactor: replace isa-on-service with multiple-dispatch helpers
May 8, 2026
0d43888
restore tests
May 8, 2026
9dd7ef6
refactor: parametric abstract types collapse paired hybrid reserve fa…
May 11, 2026
a00df13
formatting
May 11, 2026
2343c8e
rename abstract types
May 11, 2026
b962d2b
Merge remote-tracking branch 'origin/main' into ac/hybrid
May 11, 2026
8a83bce
formatting
May 11, 2026
f559b20
Merge remote-tracking branch 'origin/main' into ac/hydro-bilinear
May 12, 2026
9bc4add
Address review comments on bilinear hydro test
May 13, 2026
a73d189
Rename HydroTurbineBin2BilinearDispatch to MILP and expose bilinear_a…
May 13, 2026
57bc7ae
switch HydroTurbineMILPBilinearDispatch from n_segments to tolerance
May 28, 2026
147ec06
Replace bilinear attribute config with POM config structs
May 30, 2026
b938cbd
Merge remote-tracking branch 'origin/main' into ac/hydro-milp-rename
Jun 1, 2026
8363d0b
Simplify bilinear _iom_config bridge against updated IOM contract
Jun 1, 2026
f03f49f
Generalize bilinear configs to x×y and validate tolerance
Jun 2, 2026
929fa0c
refactor: remove const aliases, use parametric types directly
Jun 3, 2026
1d84d0f
reuse PSY.reservedirection
Jun 3, 2026
de5735d
Return to attribute-based bilinear config, deriving epigraph depth fr…
Jun 5, 2026
b4e1a6c
Merge remote-tracking branch 'origin/main' into ac/hydro-milp-rename
Jun 8, 2026
0414f81
update source branches
Jun 8, 2026
14dbbfd
removed excessive comments and tests
Jun 8, 2026
297007c
remove storage_of helper
Jun 8, 2026
e8f7333
Merge remote-tracking branch 'origin/main' into ac/hybrid
Jun 8, 2026
ccca499
Make converter loss approximations attribute-driven
Jun 8, 2026
475769b
Add relative tolerance to bilinear approximation API
Jun 8, 2026
5ebe8f6
Merge NLP+MILP bilinear formulations into single attribute-driven types
Jun 8, 2026
3919d47
Address June 8 review: centralize bilinear attrs, tighten tolerance, …
Jun 8, 2026
9d930d6
two-layer -> one-layer helper with extra noop for ambiguity
Jun 8, 2026
d954775
remove excessive comments and tests
Jun 8, 2026
1f3d6f7
copilot review
Jun 8, 2026
da98c62
fix target for approximation test
Jun 9, 2026
1b1aa08
Fix vacuous HVDC MILP/NLP agreement test
Jun 9, 2026
43465c2
remove bad test
Jun 9, 2026
44f9cf4
fix a few more reserve traits
Jun 9, 2026
4ea83cb
Merge remote-tracking branch 'origin/ac/hydro-milp-rename' into ac/hy…
Jun 9, 2026
964a665
formatting
Jun 9, 2026
4fded2c
tests run faster; use units in getters
Jun 9, 2026
46fdac2
Merge remote-tracking branch 'origin/main' into ac/hybrid
Jun 9, 2026
8aa359a
Address PR #104 June-9 review: storage reserve bug, storage-less hybr…
Jun 9, 2026
cc7aca5
Fix hybrid energy target: restore surplus/shortage slacks (soft equal…
Jun 9, 2026
3f9fa11
formatting
Jun 9, 2026
88e44b7
Apply suggestions from code review
acostarelli Jun 9, 2026
09e4a34
Merge branch 'main' into ac/hybrid
jd-lara Jun 14, 2026
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
66 changes: 66 additions & 0 deletions src/PowerOperationsModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ include("core/definitions.jl")
include("core/interfaces.jl")
include("core/default_interface_methods.jl")
include("core/physical_constant_definitions.jl")
include("core/reserve_traits.jl")
include("core/variables.jl")
include("core/expressions.jl")
include("core/constraints.jl")
Expand Down Expand Up @@ -266,6 +267,10 @@ include("services_models/reserve_group.jl")
include("services_models/transmission_interface.jl")
include("services_models/services_constructor.jl")

# Hybrid System Models (after services_models since they share reserve infrastructure)
include("hybrid_system_models/hybrid_systems.jl")
include("hybrid_system_models/hybridsystem_constructor.jl")

# Two-Terminal HVDC Models
# NOTE: AC_branches.jl and branch_constructor.jl in twoterminal_hvdc_models/ are
# identical copies of the files in ac_transmission_models/ — do NOT include them.
Expand Down Expand Up @@ -647,6 +652,67 @@ export ReserveDeploymentBalanceDownCharge
export EnergyLimitParameter
export EnergyTargetParameter

######## Hybrid System Formulations ########
export AbstractHybridFormulation
export AbstractHybridFormulationWithReserves
export HybridDispatchWithReserves

# Reserve / constraint marker traits used to parametrize hybrid + storage families.
# ConstraintBound (and UpperBound/LowerBound) come from IOM via `using InfrastructureOptimizationModels`
# and are not re-exported here to avoid name collisions with the IOM-rooted symbols.
export ReserveDirection, Up, Down
export ReserveScale, UnscaledReserve, DeployedReserve
export ReserveSide, DischargeSide, ChargeSide
export ConstraintBound

# variables
export ChargeRegularizationVariable
export DischargeRegularizationVariable
export HybridChargingReserveVariable
export HybridDischargingReserveVariable
export HybridRenewableActivePower
export HybridRenewableReserveVariable
export HybridReserveVariableIn
export HybridReserveVariableOut
export HybridStorageChargePower
export HybridStorageDischargePower
export HybridStorageReservation
export HybridThermalActivePower
export HybridThermalReserveVariable

# expressions
export HybridServedReserveInDownExpression
export HybridServedReserveInUpExpression
export HybridServedReserveOutDownExpression
export HybridServedReserveOutUpExpression
export HybridTotalReserveInDownExpression
export HybridTotalReserveInUpExpression
export HybridTotalReserveOutDownExpression
export HybridTotalReserveOutUpExpression

# constraints
export ChargeRegularizationConstraint
export DischargeRegularizationConstraint
export HybridEnergyAssetBalanceConstraint
export HybridRenewableActivePowerLimitConstraint
export HybridRenewableReserveLimitConstraint
export HybridReserveAssignmentConstraint
export HybridReserveBalanceConstraint
export HybridStatusInOnConstraint
export HybridStatusOutOnConstraint
export HybridStorageBalanceConstraint
export HybridStorageChargingReservePowerLimitConstraint
export HybridStorageDischargingReservePowerLimitConstraint
export HybridStorageStatusChargeOnConstraint
export HybridStorageStatusDischargeOnConstraint
export HybridThermalOnVariableLbConstraint
export HybridThermalOnVariableUbConstraint
export HybridThermalReserveLimitConstraint

# parameters
export HybridElectricLoadTimeSeriesParameter
export HybridRenewableActivePowerTimeSeriesParameter

#################################################################################
# Exports - Constraint Types (defined in core/constraints.jl)
#################################################################################
Expand Down
86 changes: 86 additions & 0 deletions src/core/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1100,3 +1100,89 @@ The specified constraints are formulated as:
```
"""
struct ShiftDownActivePowerVariableLimitsConstraint <: PowerVariableLimitsConstraint end

#################################################################################
# Hybrid System Constraints
#################################################################################

"""
Couples the hybrid PCC reserve variables (out + in) to the system-level
`ActivePowerReserveVariable` of each service the hybrid participates in.
"""
struct HybridReserveAssignmentConstraint <: ConstraintType end

"""
Couples the hybrid PCC reserve variables (out + in) to the sum of per-subcomponent reserve
allocations (thermal + renewable + charging + discharging).
"""
struct HybridReserveBalanceConstraint <: ConstraintType end

"""
Equates the hybrid's PCC active-power injection to the sum of internal subcomponent
flows (thermal + renewable + storage discharge - storage charge - load) net of served
reserves.
"""
struct HybridEnergyAssetBalanceConstraint <: ConstraintType end

"""
Status link between a hybrid PCC active-power variable and the reservation variable.
Parametric on [`ReserveSide`](@ref): `HybridStatusOnConstraint{DischargeSide}` is the
historical `HybridStatusOutOnConstraint`, `{ChargeSide}` is `HybridStatusInOnConstraint`.
"""
Comment thread
acostarelli marked this conversation as resolved.
struct HybridStatusOnConstraint{Sd <: ReserveSide} <: ConstraintType end
const HybridStatusOutOnConstraint = HybridStatusOnConstraint{DischargeSide}
const HybridStatusInOnConstraint = HybridStatusOnConstraint{ChargeSide}

"""
Bound between thermal subcomponent power and its commitment status (no-reserves case).
Parametric on [`ConstraintBound`](@ref): `HybridThermalOnVariableConstraint{UpperBound}`
is the historical `HybridThermalOnVariableUbConstraint`.
"""
struct HybridThermalOnVariableConstraint{B <: ConstraintBound} <: ConstraintType end
const HybridThermalOnVariableUbConstraint = HybridThermalOnVariableConstraint{UpperBound}
const HybridThermalOnVariableLbConstraint = HybridThermalOnVariableConstraint{LowerBound}

"Range constraint on thermal subcomponent power including up/down reserves."
struct HybridThermalReserveLimitConstraint <: ConstraintType end

"Upper bound on renewable subcomponent power from the time-series forecast."
struct HybridRenewableActivePowerLimitConstraint <: ConstraintType end

"Range constraint on renewable subcomponent power including up/down reserves."
struct HybridRenewableReserveLimitConstraint <: ConstraintType end

"Energy balance for the storage subcomponent of a hybrid system, including reserve deployment."
struct HybridStorageBalanceConstraint <: ConstraintType end

"""
Mutually-exclusive charge/discharge limit for the hybrid storage subcomponent
(no-reserves case). Parametric on [`ReserveSide`](@ref):
`HybridStorageStatusOnConstraint{ChargeSide}` is the historical
`HybridStorageStatusChargeOnConstraint`.
"""
struct HybridStorageStatusOnConstraint{Sd <: ReserveSide} <: ConstraintType end
const HybridStorageStatusChargeOnConstraint = HybridStorageStatusOnConstraint{ChargeSide}
const HybridStorageStatusDischargeOnConstraint =
HybridStorageStatusOnConstraint{DischargeSide}

"""
Charge- or discharge-side power limit for the hybrid storage subcomponent including
reserve carve-outs. Parametric on [`ReserveSide`](@ref):
`HybridStorageReservePowerLimitConstraint{ChargeSide}` is the historical
`HybridStorageChargingReservePowerLimitConstraint`.
"""
struct HybridStorageReservePowerLimitConstraint{Sd <: ReserveSide} <: ConstraintType end
const HybridStorageChargingReservePowerLimitConstraint =
HybridStorageReservePowerLimitConstraint{ChargeSide}
const HybridStorageDischargingReservePowerLimitConstraint =
HybridStorageReservePowerLimitConstraint{DischargeSide}

"""
Bounds the absolute charge- or discharge-power step change between consecutive time
steps, penalizing oscillation. Active only when the hybrid `\"regularization\"`
attribute is set. Parametric on [`ReserveSide`](@ref):
`RegularizationConstraint{ChargeSide}` is the historical `ChargeRegularizationConstraint`.
"""
struct RegularizationConstraint{Sd <: ReserveSide} <: ConstraintType end
const ChargeRegularizationConstraint = RegularizationConstraint{ChargeSide}
const DischargeRegularizationConstraint = RegularizationConstraint{DischargeSide}
107 changes: 90 additions & 17 deletions src/core/expressions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,25 +78,98 @@ of the energy balance for the system in medium term planning
struct EnergyBalanceExpression <: ExpressionType end

#################################################################################
# Energy Storage Expressions
# Energy Storage / Hybrid Reserve Aggregation Expressions
#
# A single parametric family covers both the hybrid PCC boundary aggregation
# (HybridPCCReserveExpression) and the storage-subcomponent balance aggregation
# (StorageReserveBalanceExpression). The three axes are:
# D <: ReserveDirection : Up | Down
# S <: ReserveScale : UnscaledReserve (multiplier 1.0)
# | DeployedReserve (multiplier = get_deployed_fraction(s))
# Sd <: ReserveSide : DischargeSide (PCC "Out" / storage "Discharge")
# | ChargeSide (PCC "In" / storage "Charge")
# Each of the 16 historical singletons is retained as a const alias for an exact
# parametrization, so all existing imports and `get_expression(container, T, V)`
# calls continue to work unchanged.
#################################################################################

"""
Per-device, per-service aggregation of the reserve quantity offered by a storage device
(or the storage subcomponent of a hybrid system). One container is created per service
participated in, and the per-component reserve variables (charging + discharging) are
summed into it. Consumed by [`HybridReserveBalanceConstraint`](@ref) and used as the
right-hand side of the system-level reserve balance.
"""
struct TotalReserveOffering <: ExpressionType end

abstract type StorageReserveDischargeExpression <: ExpressionType end
abstract type StorageReserveChargeExpression <: ExpressionType end

# Used for the Power Limits constraints
struct ReserveAssignmentBalanceUpDischarge <: StorageReserveDischargeExpression end
struct ReserveAssignmentBalanceUpCharge <: StorageReserveChargeExpression end
struct ReserveAssignmentBalanceDownDischarge <: StorageReserveDischargeExpression end
struct ReserveAssignmentBalanceDownCharge <: StorageReserveChargeExpression end

# Used for the SoC estimates
struct ReserveDeploymentBalanceUpDischarge <: StorageReserveDischargeExpression end
struct ReserveDeploymentBalanceUpCharge <: StorageReserveChargeExpression end
struct ReserveDeploymentBalanceDownDischarge <: StorageReserveDischargeExpression end
struct ReserveDeploymentBalanceDownCharge <: StorageReserveChargeExpression end
abstract type ReserveAggregationExpression{
D <: ReserveDirection,
S <: ReserveScale,
Sd <: ReserveSide,
} <: ExpressionType end

"""
Hybrid-boundary aggregation of reserve quantities offered through the discharge (out) and
charge (in) sides of a `PSY.HybridSystem`. Concrete parametrizations of the three axes
(Direction / Scale / Side) are exposed as the historical alias names below.
"""
struct HybridPCCReserveExpression{D, S, Sd} <:
ReserveAggregationExpression{D, S, Sd} end

"""
Aggregation of reserve variables allocated to the storage subcomponent of a hybrid system
(or a standalone storage device). Concrete parametrizations of the three axes
(Direction / Scale / Side) are exposed as the historical alias names below.
"""
struct StorageReserveBalanceExpression{D, S, Sd} <:
ReserveAggregationExpression{D, S, Sd} end
Comment thread
acostarelli marked this conversation as resolved.

# Historical hybrid PCC names retained as const aliases.
const HybridTotalReserveOutUpExpression =
HybridPCCReserveExpression{Up, UnscaledReserve, DischargeSide}
const HybridTotalReserveOutDownExpression =
HybridPCCReserveExpression{Down, UnscaledReserve, DischargeSide}
const HybridTotalReserveInUpExpression =
HybridPCCReserveExpression{Up, UnscaledReserve, ChargeSide}
const HybridTotalReserveInDownExpression =
HybridPCCReserveExpression{Down, UnscaledReserve, ChargeSide}
const HybridServedReserveOutUpExpression =
HybridPCCReserveExpression{Up, DeployedReserve, DischargeSide}
const HybridServedReserveOutDownExpression =
HybridPCCReserveExpression{Down, DeployedReserve, DischargeSide}
const HybridServedReserveInUpExpression =
HybridPCCReserveExpression{Up, DeployedReserve, ChargeSide}
const HybridServedReserveInDownExpression =
HybridPCCReserveExpression{Down, DeployedReserve, ChargeSide}

# Historical storage balance names retained as const aliases.
const ReserveAssignmentBalanceUpDischarge =
StorageReserveBalanceExpression{Up, UnscaledReserve, DischargeSide}
const ReserveAssignmentBalanceDownDischarge =
StorageReserveBalanceExpression{Down, UnscaledReserve, DischargeSide}
const ReserveAssignmentBalanceUpCharge =
StorageReserveBalanceExpression{Up, UnscaledReserve, ChargeSide}
const ReserveAssignmentBalanceDownCharge =
StorageReserveBalanceExpression{Down, UnscaledReserve, ChargeSide}
const ReserveDeploymentBalanceUpDischarge =
StorageReserveBalanceExpression{Up, DeployedReserve, DischargeSide}
const ReserveDeploymentBalanceDownDischarge =
StorageReserveBalanceExpression{Down, DeployedReserve, DischargeSide}
const ReserveDeploymentBalanceUpCharge =
StorageReserveBalanceExpression{Up, DeployedReserve, ChargeSide}
const ReserveDeploymentBalanceDownCharge =
StorageReserveBalanceExpression{Down, DeployedReserve, ChargeSide}

# Role-based Union aliases retained for callers that match by scale (Total/Served)
# or by storage side (Charge/Discharge) rather than by direction.
const HybridTotalReserveExpression =
HybridPCCReserveExpression{<:ReserveDirection, UnscaledReserve, <:ReserveSide}
const HybridServedReserveExpression =
HybridPCCReserveExpression{<:ReserveDirection, DeployedReserve, <:ReserveSide}
const StorageReserveDischargeExpression =
StorageReserveBalanceExpression{<:ReserveDirection, <:ReserveScale, DischargeSide}
const StorageReserveChargeExpression =
StorageReserveBalanceExpression{<:ReserveDirection, <:ReserveScale, ChargeSide}

# Method extensions for output writing
should_write_resulting_value(::Type{InterfaceTotalFlow}) = true
Expand All @@ -108,8 +181,8 @@ should_write_resulting_value(::Type{HydroServedReserveDownExpression}) = true
should_write_resulting_value(::Type{TotalHydroFlowRateReservoirOutgoing}) = true
should_write_resulting_value(::Type{TotalHydroFlowRateTurbineOutgoing}) = true

should_write_resulting_value(::Type{StorageReserveDischargeExpression}) = true
should_write_resulting_value(::Type{StorageReserveChargeExpression}) = true
should_write_resulting_value(::Type{<:StorageReserveBalanceExpression}) = true
should_write_resulting_value(::Type{<:HybridServedReserveExpression}) = true

# Method extensions for unit conversion
convert_output_to_natural_units(::Type{InterfaceTotalFlow}) = true
Expand Down
Loading
Loading