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
42 changes: 42 additions & 0 deletions src/InfrastructureOptimizationModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ using DocStringExtensions
# Base Models
export DecisionModel
export EmulationModel
export InvestmentModel, InvestmentModelStore
export AbstractProblemTemplate
export ServicesModelContainer, DevicesModelContainer, BranchModelContainer
export InitialCondition
Expand Down Expand Up @@ -191,6 +192,8 @@ export FixedOutput
export get_device_cache
export AbstractEventModel, AbstractEventKey
export get_events, set_event_model!
export TechnologyModel
export RequirementModel

# Parameter Container Infrastructure
export ParameterContainer
Expand All @@ -207,6 +210,7 @@ export validate_time_series!
export init_optimization_container!
## Op Model Exports
export get_initial_conditions
export get_initial_condition!
export serialize_outputs
export serialize_optimization_model

Expand Down Expand Up @@ -470,6 +474,11 @@ export NetActivePower
export DCCurrentBalance
export HVDCPowerBalance

export InvestmentExpressionType
export OperationsExpressionType
export FeasibilityExpressionType
export CumulativeInvestmentExpressionType

# Standard Variable Types (used in IOM infrastructure code, consumed by POM)
export ActivePowerVariable, ActivePowerInVariable, ActivePowerOutVariable
export PowerAboveMinimumVariable
Expand All @@ -478,6 +487,11 @@ export ReservationVariable
export PiecewiseLinearCostVariable
export RateofChangeConstraintSlackUp, RateofChangeConstraintSlackDown
export DCVoltage

# Standard Variable Types (used in IOM infrastructure code, consumed by PSIN)
export InvestmentVariableType, OperationsVariableType, FeasibilityVariableType
export BuildInvestmentVariableType

# Abstract types needed by POM for type hierarchy
export SparseVariableType, InterpolationVariableType, BinaryInterpolationVariableType

Expand Down Expand Up @@ -514,6 +528,11 @@ export SimulationBuildStatus
# Problem Types
export AbstractOptimizationProblem

# Technology and Requirement Formulations
export InvestmentTechnologyFormulation,
OperationsTechnologyFormulation, FeasibilityTechnologyFormulation
export RequirementFormulation

# Settings and Data Types
export Settings
export get_warm_start
Expand All @@ -530,6 +549,20 @@ export set_horizon!, set_initial_time!, set_warm_start!
export log_values
export InitialConditionsData

# Time Mapping
export TimeMapping, OperationalPeriods, InvestmentIntervals
export get_consecutive_slices, get_operational_indexes, get_feasibility_indexes
export get_all_indexes, get_time_stamps, get_investment_time_stamps
export get_inverse_invest_mapping, get_base_date
export get_total_period_count, get_total_operation_period_count
export get_total_feasibility_period_count, get_total_investment_period_count
export get_time_steps,
get_operational_time_steps, get_feasibility_time_steps, get_investment_time_steps
export is_feasibility_empty, get_investment_map_to_operational_slices
export get_initial_condition!
export set_investment_data!, InvestmentContainerData
export TransportModel, get_use_slacks, AbstractTransportAggregation

# Constants
export COST_EPSILON
export INITIALIZATION_PROBLEM_HORIZON_COUNT
Expand Down Expand Up @@ -590,6 +623,8 @@ include("core/outputs_by_time.jl")

# Order Required
include("operation/problem_template.jl")
include("core/time_mapping.jl")
include("investments/container_data.jl")
include("core/optimization_container.jl")
include("core/dual_processing.jl")

Expand Down Expand Up @@ -668,6 +703,13 @@ include("operation/time_series_interface.jl")
include("operation/optimization_debugging.jl")
include("operation/model_numerical_analysis_utils.jl")

include("investments/formulations.jl")
include("investments/technology_model.jl")
include("investments/requirement_model.jl")
include("investments/investment_model_store.jl")
include("investments/investment_model.jl")
include("investments/transport_model.jl")

include("initial_conditions/calculate_initial_condition.jl")

# Utils
Expand Down
91 changes: 91 additions & 0 deletions src/core/optimization_container.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ mutable struct OptimizationContainer <: AbstractOptimizationContainer
JuMPmodel::JuMP.Model
time_steps::UnitRange{Int}
settings::Settings
investment_data::Union{Nothing, InvestmentContainerData}

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.

We need to find a different home for this

variables::OrderedDict{VariableKey, JuMPArray}
aux_variables::OrderedDict{AuxVarKey, JuMPArray}
duals::OrderedDict{ConstraintKey, JuMPArray}
Expand Down Expand Up @@ -122,6 +123,7 @@ function OptimizationContainer(
isnothing(jump_model) ? JuMP.Model() : jump_model,
1:1,
settings,
nothing,
OrderedDict{VariableKey, JuMPArray}(),
OrderedDict{AuxVarKey, JuMPArray}(),
OrderedDict{ConstraintKey, JuMPArray}(),
Expand Down Expand Up @@ -194,6 +196,59 @@ get_objective_expression(container::OptimizationContainer) = container.objective

get_serialization_task(container::OptimizationContainer) = container.serialization_task

get_investment_data(container::OptimizationContainer) = container.investment_data
set_investment_data!(container::OptimizationContainer, data::InvestmentContainerData) =
container.investment_data = data

get_time_mapping(container::OptimizationContainer) =
get_investment_data(container).time_mapping
get_operational_weights(container::OptimizationContainer) =
get_investment_data(container).operational_weights
get_base_year(container::OptimizationContainer) =
get_investment_data(container).base_year
get_discount_rate(container::OptimizationContainer) =
get_investment_data(container).discount_rate
get_inflation_rate(container::OptimizationContainer) =
get_investment_data(container).inflation_rate
get_interest_rate(container::OptimizationContainer) =
get_investment_data(container).interest_rate

function set_time_mapping!(
container::OptimizationContainer,
time_mapping::TimeMapping,
)
get_investment_data(container).time_mapping = time_mapping
return
end

function set_operational_weights!(
container::OptimizationContainer,
operational_weights::Union{Nothing, Vector{Float64}},
)
get_investment_data(container).operational_weights = operational_weights
return
end

function set_base_year!(container::OptimizationContainer, base_year::Int)
get_investment_data(container).base_year = base_year
return
end

function set_discount_rate!(container::OptimizationContainer, discount_rate::Float64)
get_investment_data(container).discount_rate = discount_rate
return
end

function set_inflation_rate!(container::OptimizationContainer, inflation_rate::Float64)
get_investment_data(container).inflation_rate = inflation_rate
return
end

function set_interest_rate!(container::OptimizationContainer, interest_rate::Float64)
get_investment_data(container).interest_rate = interest_rate
return
end

function set_serialization_task!(container::OptimizationContainer, task::Task)
container.serialization_task = task
return
Expand Down Expand Up @@ -371,6 +426,42 @@ function init_optimization_container!(
return
end

function init_optimization_container!(
container::OptimizationContainer,
template::AbstractProblemTemplate,
portfolio::IS.InfrastructureSystemsContainer,
)
# The order of operations matter
transport_model = get_transport_model(template)
settings = get_settings(container)

# Update Time Mapping
capital_model = get_capital_model(template)
operation_model = get_operation_model(template)
feasibility_model = get_feasibility_model(template)

time_map = TimeMapping(
capital_model.investment_years,
operation_model.representative_series,
feasibility_model.sample_periods,
)

set_time_mapping!(container, time_map)
set_operational_weights!(container, operation_model.series_weights)

# Set Financial Data in Container from Portfolio
set_base_year!(container, portfolio.financial_data.base_year)
set_discount_rate!(container, portfolio.financial_data.discount_rate)
set_inflation_rate!(container, portfolio.financial_data.inflation_rate)
set_interest_rate!(container, portfolio.financial_data.interest_rate)

stats = get_optimizer_stats(container)
stats.detailed_stats = get_detailed_optimizer_stats(settings)

finalize_jump_model!(container, settings)
return
end

function reset_optimization_model!(container::OptimizationContainer)
for field in [:variables, :aux_variables, :constraints, :expressions, :duals]
empty!(getfield(container, field))
Expand Down
4 changes: 4 additions & 0 deletions src/core/settings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ function Settings(
)
end

# System-less convenience constructor (uses the `nothing`-"system" accessor defaults). Useful for
# building a bare container (e.g. unit tests) without a domain system object.
Settings(; kwargs...) = Settings(nothing; kwargs...)

function log_values(settings::Settings)
text = Vector{String}()
for (name, type) in zip(fieldnames(Settings), fieldtypes(Settings))
Expand Down
22 changes: 22 additions & 0 deletions src/core/standard_variables_expressions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,25 @@ function add_to_expression!(
"add_to_expression! not implemented for expression_type=$expression_type, variable_type=$variable_type, device_type=$(typeof(devices.values[1]))",
)
end

####################################################################################
# Standard Variable and Expression Types for Investment and Operations Formulations
# Only types that IOM's own infrastructure code references belong here.
# Technology-specific variable types are defined in PowerSystemsInvestments.jl.
####################################################################################

abstract type InvestmentExpressionType <: ExpressionType end
abstract type OperationsExpressionType <: ExpressionType end
abstract type FeasibilityExpressionType <: ExpressionType end

abstract type CumulativeInvestmentExpressionType <: InvestmentExpressionType end

#TODO: Determine which of the remaining expressions and variables should be ported

abstract type InvestmentVariableType <: VariableType end
abstract type OperationsVariableType <: VariableType end
abstract type FeasibilityVariableType <: VariableType end

### Investment Variables ###

abstract type BuildInvestmentVariableType <: InvestmentVariableType end
137 changes: 137 additions & 0 deletions src/core/time_mapping.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
struct InvestmentIntervals

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.

WE should be able to generalize this concept

time_stamps::Vector{NTuple{2, Dates.Date}}
map_to_operational_slices::Dict{Int, Vector{Int}}
map_to_feasibility_slices::Dict{Int, Vector{Int}}
end

function InvestmentIntervals(::Nothing)
return InvestmentIntervals(
Vector{NTuple{2, Dates.Date}}(),
Dict{Int, Vector{Int}}(),
Dict{Int, Vector{Int}}(),
)
end

struct OperationalPeriods
time_stamps::Vector{Dates.DateTime}
consecutive_slices::Vector{Vector{Int}}
inverse_invest_mapping::Vector{Int}
feasibility_indexes::Vector{Int}
operational_indexes::Vector{Int}
end

function OperationalPeriods(::Nothing)
return OperationalPeriods(
Vector{Dates.DateTime}(),
Vector{Vector{Int}}(),
Vector{Int}(),
Vector{Int}(),
Vector{Int}(),
)
end

struct TimeMapping
investment::InvestmentIntervals
operation::OperationalPeriods
end

function TimeMapping(
investment_intervals::Vector{NTuple{2, Dates.Date}},
operational_periods::Vector{Vector{Dates.DateTime}},
feasibility_periods::Vector{Vector{Dates.DateTime}},
)
# TODO:
# Validation of the dates to avoid overlaps
# Validation of the dates to avoid gaps in the operational periods

op_index_last_slice = length(operational_periods)
all_operation_slices = [operational_periods; feasibility_periods]
total_count = sum(length(x) for x in all_operation_slices)
total_slice_count = length(operational_periods) + length(feasibility_periods)
time_stamps = Vector{Dates.DateTime}(undef, total_count)
consecutive_slices = Vector{Vector{Int}}(undef, total_slice_count)
inverse_invest_mapping = Vector{Int}(undef, total_slice_count)
map_to_operational_slices =
Dict{Int, Vector{Int}}(i => Vector{Int}() for i in 1:length(investment_intervals))
map_to_feasibility_slices =
Dict{Int, Vector{Int}}(i => Vector{Int}() for i in 1:length(investment_intervals))

ix = 1
slice_running_count = 0
for (sx, slice) in enumerate(all_operation_slices)
slice_length = length(slice)
slice_found_in_interval = false
for (ivx, investment_interval) in enumerate(investment_intervals)
if first(slice) >= investment_interval[1] &&
last(slice) <= investment_interval[2]
if sx <= op_index_last_slice
push!(map_to_operational_slices[ivx], sx)
else
push!(map_to_feasibility_slices[ivx], sx)
end
inverse_invest_mapping[sx] = ivx
slice_found_in_interval = true
break
end
end
if !slice_found_in_interval
error()
end
slice_length = length(slice)
slice_indeces = range(slice_running_count + 1; length = slice_length)
consecutive_slices[sx] = collect(slice_indeces)
slice_running_count = last(slice_indeces)
for time_stamp in slice
time_stamps[ix] = time_stamp
ix += 1
end
end

op_periods = OperationalPeriods(
time_stamps,
consecutive_slices,
inverse_invest_mapping,
collect(range(; start = op_index_last_slice + 1, stop = total_slice_count)),
collect(range(1, op_index_last_slice)),
)

inv_periods = InvestmentIntervals(
investment_intervals,
map_to_operational_slices,
map_to_feasibility_slices,
)

TimeMapping(inv_periods, op_periods)
end

get_consecutive_slices(tm::TimeMapping) = tm.operation.consecutive_slices
get_operational_indexes(tm::TimeMapping) = tm.operation.operational_indexes
get_feasibility_indexes(tm::TimeMapping) = tm.operation.feasibility_indexes
get_all_indexes(tm::TimeMapping) =
[tm.operation.operational_indexes; tm.operation.feasibility_indexes]
get_time_stamps(tm::TimeMapping) = tm.operation.time_stamps
get_investment_time_stamps(tm::TimeMapping) = tm.investment.time_stamps
get_inverse_invest_mapping(tm::TimeMapping) = tm.operation.inverse_invest_mapping
get_base_date(tm::TimeMapping) = first(tm.investment.time_stamps)[1]
get_total_period_count(tm::TimeMapping) = length(tm.operation.time_stamps)
function get_total_operation_period_count(tm::TimeMapping)
consecutive_slices = get_consecutive_slices(tm)
operational_indexes = get_operational_indexes(tm)
return last(consecutive_slices[last(operational_indexes)])
end
function get_total_feasibility_period_count(tm::TimeMapping)
consecutive_slices = get_consecutive_slices(tm)
return last(last(consecutive_slices))
end
get_total_investment_period_count(tm::TimeMapping) = length(tm.investment.time_stamps)
get_time_steps(tm::TimeMapping) = 1:get_total_period_count(tm)
get_operational_time_steps(tm::TimeMapping) = 1:get_total_operation_period_count(tm)
get_feasibility_time_steps(tm::TimeMapping) =
(get_total_operation_period_count(tm) + 1):get_total_feasibility_period_count(tm)
get_investment_time_steps(tm::TimeMapping) = 1:get_total_investment_period_count(tm)
is_feasibility_empty(tm::TimeMapping) = isempty(tm.operation.feasibility_indexes)
get_investment_map_to_operational_slices(tm::TimeMapping) =
tm.investment.map_to_operational_slices
function TimeMapping(::Nothing)
return TimeMapping(InvestmentIntervals(nothing), OperationalPeriods(nothing))
end
Loading
Loading