Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/InfrastructureOptimizationModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ export add_sparse_pwl_interpolation_variables!
export JuMPOrFloat
# Constraint helpers
export add_range_constraints!, add_parameterized_upper_bound_range_constraints
export add_reserve_bound_range_constraints!
export add_reserve_bound_range_constraints!, add_commitment_bound_range_constraints!
export add_semicontinuous_range_constraints!, add_semicontinuous_ramp_constraints!
# Cost helpers
export add_shut_down_cost!, add_start_up_cost!
Expand Down
3 changes: 2 additions & 1 deletion src/common_models/add_constraint_dual.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ function add_constraint_dual!(
model::NetworkModel{T},
) where {T <: AbstractPowerModel}
if !isempty(get_duals(model))
devices = get_available_components(model, IS.InfrastructureSystemsComponent, sys)
# component is ACBus, but we don't have PSY as a dependency.
devices = get_available_components(model, component_for_network_dual(nothing), sys)
Comment thread
luke-kiernan marked this conversation as resolved.
for constraint_type in get_duals(model)
assign_dual_variable!(container, constraint_type, devices, model)
end
Expand Down
46 changes: 46 additions & 0 deletions src/common_models/interfaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,40 @@ For thermals, equivalent to `get_must_run`, but that implementation belongs in P
"""
skip_proportional_cost(d::IS.InfrastructureSystemsComponent) = false

###############################
#### System query stubs #######
###############################
# Extension points for querying a system object. POM provides methods for
# PSY.System; tests provide methods for MockSystem. IOM itself never accesses
# sys.data.

"Extension point: time-series resolutions available on the system."
function get_time_series_resolutions end

"Extension point: counts summary of time series on the system."
function get_time_series_counts end

"Extension point: counts by component type of time series on the system."
function get_time_series_counts_by_type end

"Extension point: forecast interval configured on the system."
function get_forecast_interval end

"Extension point: forecast horizon configured on the system."
function get_forecast_horizon end
Comment thread
luke-kiernan marked this conversation as resolved.

"Extension point: summary table of forecasts on the system."
function get_forecast_summary_table end

"Extension point: transform single time series into deterministic forecasts on the system."
function transform_single_time_series! end

Comment on lines +156 to +175
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think several of these will cause name clashes with IS no ? Or are methods that should live in IS

"Extension point: stable UUID for the system (used as a filename identifier)."
function get_system_uuid end

"Extension point: get components of type `T` in a subsystem of the system."
function get_subsystem_components end

###############################
###### Start-up Cost ##########
###############################
Expand Down Expand Up @@ -245,3 +279,15 @@ Only called in `emulation_model.jl`: that file's contents and this function shou
likely be moved to POM or PSI.
"""
function update_container_parameter_values! end

"""
Component type associated with network duals. Returns ACBus when passed in `nothing`.
Only called in a single spot in `add_constraint_dual!` for the network model.
"""
function component_for_network_dual end

"""
Component type associated with hvdc interpolation constraints. Returns DCBus when passed in
`nothing`. Only called in a single spot in `incremental.jl`.
"""
function component_for_hvdc_interpolation end
Comment thread
luke-kiernan marked this conversation as resolved.
25 changes: 22 additions & 3 deletions src/common_models/range_constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,18 @@ function add_semicontinuous_range_constraints!(
return
end

# Generic component version - always uses binary variable
# Generic component version - always uses binary variable.
# `meta_suffix` is appended to the default constraint meta so callers can stack a second
# OnVariable-keyed bound alongside another bound constraint (e.g. a reservation-keyed
# one) on the same `(T, V)` without a meta collision — see `add_commitment_bound_range_constraints!`.
function _add_semicontinuous_bound_range_constraints_impl!(
container::OptimizationContainer,
::Type{T},
dir::BoundDirection,
array,
devices::Union{Vector{V}, IS.FlattenIteratorWrapper{V}},
::DeviceModel{V, W},
::DeviceModel{V, W};
meta_suffix::String = "",
) where {
T <: ConstraintType,
V <: IS.InfrastructureSystemsComponent,
Expand All @@ -212,7 +216,7 @@ function _add_semicontinuous_bound_range_constraints_impl!(
names = IS.get_name.(devices)
jump_model = get_jump_model(container)
con = add_constraints_container!(
container, T, V, names, time_steps; meta = constraint_meta(dir))
container, T, V, names, time_steps; meta = constraint_meta(dir) * meta_suffix)
varbin = get_variable(container, OnVariable, V)

for device in devices, t in time_steps
Expand All @@ -225,6 +229,21 @@ function _add_semicontinuous_bound_range_constraints_impl!(
return
end

# Exported wrapper: use this from downstream packages to add an OnVariable-keyed bound
# alongside another bound constraint on the same `(T, V)` key — pass `meta_suffix = "_aux"`
# (or similar) to avoid colliding with the default "lb"/"ub" meta.
add_commitment_bound_range_constraints!(
container::OptimizationContainer,
::Type{T},
dir::BoundDirection,
array,
devices,
model::DeviceModel;
meta_suffix::String = "",
) where {T <: ConstraintType} =
_add_semicontinuous_bound_range_constraints_impl!(
container, T, dir, array, devices, model; meta_suffix)

# Unified reserve range constraints impl
# invert_binary: true for InputActivePower (uses 1-varbin), false for others (uses varbin)
function add_reserve_bound_range_constraints!(
Expand Down
2 changes: 1 addition & 1 deletion src/core/optimization_container.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ function init_optimization_container!(
# set_initial_time!(settings, IS.get_forecast_initial_timestamp(sys))
set_initial_time!(settings, temp_get_forecast_initial_timestamp(sys))
elseif get_default_time_series_type(container) <: IS.SingleTimeSeries
ini_time, _ = IS.check_time_series_consistency(sys, IS.SingleTimeSeries)
ini_time, _ = IS.check_time_series_consistency(sys.data, IS.SingleTimeSeries)
set_initial_time!(settings, ini_time)
end
end
Expand Down
3 changes: 1 addition & 2 deletions src/core/optimization_problem_outputs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,8 @@ get_optimizer_stats(res::OptimizationProblemOutputs) = res.optimizer_stats
get_parameter_values(res::OptimizationProblemOutputs) = res.parameter_values
get_source_data(res::OptimizationProblemOutputs) = res.source_data

# FIXME get_uuid declare as stub
make_system_filename(sys::IS.InfrastructureSystemsContainer) =
make_system_filename(IS.get_uuid(sys.data.internal))
make_system_filename(get_system_uuid(sys))
make_system_filename(sys_uuid::Union{Base.UUID, AbstractString}) = "system-$(sys_uuid).json"

"""
Expand Down
3 changes: 1 addition & 2 deletions src/core/settings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ function Settings(
store_variable_names = false,
ext = Dict{String, Any}(),
)
# alternatively, declare as stub and implement in POM.
if time_series_cache_size > 0 && IS.stores_time_series_in_memory(sys.data)
if time_series_cache_size > 0 && stores_time_series_in_memory(sys)
@info "Overriding time_series_cache_size because time series is stored in memory"
time_series_cache_size = 0
end
Expand Down
28 changes: 15 additions & 13 deletions src/objective_function/proportional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
# this is only used for ControllableLoads with non-PowerLoadInterruptible formulations.
# The rest go through a thin wrapper around the maybe-variant version.
"""
Default implementation for proportional cost, where the cost term is not time variant. Anything
time-varying should implement its own method.
Default implementation for proportional cost, where the cost term is not time variant.
See also: `add_proportional_cost_maybe_time_variant!` for a common basis for devices that
might have time-variant proportional costs.
"""
function add_proportional_cost!(
container::OptimizationContainer,
Expand All @@ -18,25 +19,26 @@ function add_proportional_cost!(
U <: VariableType,
V <: AbstractDeviceFormulation,
}
# NOTE: anything time-varying should implement its own method.
multiplier = objective_function_multiplier(U, V)
for d in devices
op_cost_data = get_operation_cost(d)
cost_term = proportional_cost(op_cost_data, U, d, V)
iszero(cost_term) && continue
name = get_name(d)
rate = cost_term * multiplier
skip = skip_proportional_cost(d)
for t in get_time_steps(container)
variable = get_variable(container, U, T)[name, t]
add_cost_term_invariant!(
container,
variable,
rate,
ProductionCostExpression,
T,
name,
t,
)
if skip
# must-run etc.: bookkeep in ProductionCostExpression but not in objective
add_cost_to_expression!(
container, ProductionCostExpression, rate, T, name, t)
else
variable = get_variable(container, U, T)[name, t]
add_cost_term_invariant!(
container, variable, rate,
ProductionCostExpression, T, name, t,
)
end
end
end
return
Expand Down
13 changes: 6 additions & 7 deletions src/operation/decision_model.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function get_deterministic_time_series_type(sys::IS.InfrastructureSystemsContainer)
time_series_types = IS.get_time_series_counts_by_type(sys.data)
time_series_types = get_time_series_counts_by_type(sys)
existing_types = Set(d["type"] for d in time_series_types)
if ("Deterministic" in existing_types) &&
("DeterministicSingleTimeSeries" in existing_types)
Expand Down Expand Up @@ -242,12 +242,11 @@ function init_model_store_params!(model::DecisionModel)
if model_interval != UNSET_INTERVAL
interval = model_interval
else
interval = IS.get_forecast_interval(system.data)
interval = get_forecast_interval(system)
end
resolution = get_resolution(model)
base_power = get_base_power(system)
# FIXME declare as stub
sys_uuid = IS.get_uuid(system.data.internal)
sys_uuid = get_system_uuid(system)
store_params = ModelStoreParams(
num_executions,
horizon,
Expand All @@ -264,7 +263,7 @@ end
function validate_time_series!(model::DecisionModel{<:DefaultDecisionProblem})
sys = get_system(model)
settings = get_settings(model)
available_resolutions = IS.get_time_series_resolutions(sys.data)
available_resolutions = get_time_series_resolutions(sys)

if get_resolution(settings) == UNSET_RESOLUTION && length(available_resolutions) != 1
throw(
Expand Down Expand Up @@ -307,11 +306,11 @@ function validate_time_series!(model::DecisionModel{<:DefaultDecisionProblem})
if get_horizon(settings) == UNSET_HORIZON
set_horizon!(
settings,
IS.get_forecast_horizon(sys.data; interval = _to_is_interval(model_interval)),
get_forecast_horizon(sys; interval = _to_is_interval(model_interval)),
)
end

counts = IS.get_time_series_counts(sys.data)
counts = get_time_series_counts(sys)
if counts.forecast_count < 1
error(
"The system does not contain forecast data. A DecisionModel can't be built.",
Expand Down
7 changes: 3 additions & 4 deletions src/operation/emulation_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ end
function validate_time_series!(model::EmulationModel{<:DefaultEmulationProblem})
sys = get_system(model)
settings = get_settings(model)
available_resolutions = IS.get_time_series_resolutions(sys.data)
available_resolutions = get_time_series_resolutions(sys)

if get_resolution(settings) == UNSET_RESOLUTION && length(available_resolutions) != 1
throw(
Expand All @@ -236,7 +236,7 @@ function validate_time_series!(model::EmulationModel{<:DefaultEmulationProblem})
set_horizon!(settings, get_resolution(settings))
end

counts = IS.get_time_series_counts(sys.data)
counts = get_time_series_counts(sys)
if counts.static_time_series_count < 1
error(
"The system does not contain Static Time Series data. A EmulationModel can't be built.",
Expand All @@ -258,8 +258,7 @@ function init_model_store_params!(model::EmulationModel)
settings = get_settings(model)
horizon = interval = resolution = get_resolution(settings)
base_power = get_base_power(system)
# FIXME declare as stub
sys_uuid = IS.get_uuid(system.data.internal)
sys_uuid = get_system_uuid(system)
set_store_params!(
get_internal(model),
ModelStoreParams(
Expand Down
8 changes: 6 additions & 2 deletions src/quadratic_approximations/incremental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,12 @@ function _add_generic_incremental_interpolation_constraint!(

# Retrieve all required variables from the optimization container
# Retrieve original variable for DCVoltage from the Bus
# FIXME edge case: DCVoltage. W is InterconnectingConverter but key says DCBus.
x_var = get_variable(container, R, W) # Original variable (domain of function)
if R <: DCVoltage
# workaround for the fact that we can't write PSY.DCBus.
x_var = get_variable(container, R, component_for_hvdc_interpolation(nothing))
else
x_var = get_variable(container, R, W) # Original variable (domain of function)
end
Comment thread
luke-kiernan marked this conversation as resolved.
y_var = get_variable(container, S, W) # Approximated variable (range of function)
δ_var = get_variable(container, T, W) # Interpolation variables (weights for segments)
z_var = get_variable(container, U, W) # Binary variables (ordering constraints)
Expand Down
16 changes: 5 additions & 11 deletions src/utils/component_utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,7 @@ function get_available_components(
sys::IS.InfrastructureSystemsContainer,
) where {T <: IS.InfrastructureSystemsComponent}
subsystem = get_subsystem(model)
# FIXME have to patch thru to sys.data here
return IS.get_components(
T,
sys.data;
subsystem_name = subsystem,
)
return get_subsystem_components(T, sys; subsystem_name = subsystem)
end

##################################################
Expand Down Expand Up @@ -305,7 +300,7 @@ end
is_time_variant(x) = IS.is_time_series_backed(x)

function get_forecast_intervals(sys::IS.InfrastructureSystemsContainer)
table = IS.get_forecast_summary_table(sys.data)
table = get_forecast_summary_table(sys)
return Set(row.interval for row in eachrow(table) if row.interval !== nothing)
end

Expand All @@ -319,7 +314,7 @@ function auto_transform_time_series!(
return
end

counts = IS.get_time_series_counts(sys.data)
counts = get_time_series_counts(sys)
if counts.static_time_series_count < 1
return
end
Expand All @@ -333,9 +328,8 @@ function auto_transform_time_series!(

@info "Auto-transforming SingleTimeSeries to DeterministicSingleTimeSeries" horizon =
Dates.canonicalize(model_horizon) interval = Dates.canonicalize(model_interval)
IS.transform_single_time_series!(
sys.data,
IS.DeterministicSingleTimeSeries,
transform_single_time_series!(
sys,
model_horizon,
model_interval;
delete_existing = false,
Expand Down
Loading
Loading