From 107dc618a8b9dd95e1449387d1a11a3faaa97b49 Mon Sep 17 00:00:00 2001 From: Jerry Potts <37674411+jerrypotts@users.noreply.github.com> Date: Thu, 18 Jun 2026 08:16:43 -0600 Subject: [PATCH 1/5] port abstract types for expressions, variables --- src/InfrastructureOptimizationModels.jl | 12 ++++++++++++ src/core/standard_variables_expressions.jl | 22 ++++++++++++++++++++++ src/investments/formulations.jl | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 src/investments/formulations.jl diff --git a/src/InfrastructureOptimizationModels.jl b/src/InfrastructureOptimizationModels.jl index a9b18d28..b7b75217 100644 --- a/src/InfrastructureOptimizationModels.jl +++ b/src/InfrastructureOptimizationModels.jl @@ -457,6 +457,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 @@ -465,6 +470,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 @@ -653,6 +663,8 @@ include("operation/time_series_interface.jl") include("operation/optimization_debugging.jl") include("operation/model_numerical_analysis_utils.jl") +include("investments/formulations.jl") + include("initial_conditions/calculate_initial_condition.jl") # Utils diff --git a/src/core/standard_variables_expressions.jl b/src/core/standard_variables_expressions.jl index 3f862bb5..170479e8 100644 --- a/src/core/standard_variables_expressions.jl +++ b/src/core/standard_variables_expressions.jl @@ -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 \ No newline at end of file diff --git a/src/investments/formulations.jl b/src/investments/formulations.jl new file mode 100644 index 00000000..f561d27b --- /dev/null +++ b/src/investments/formulations.jl @@ -0,0 +1,2 @@ +# Should get moved to IS.Optimization and then re-exported by IOM +abstract type AbstractTechnologyFormulation end \ No newline at end of file From 2c480224bb522ba38042a4c20df5df0f18e50853 Mon Sep 17 00:00:00 2001 From: Jerry Potts Date: Tue, 22 Sep 2026 13:22:21 -0400 Subject: [PATCH 2/5] port model structs and abstract types for investment models --- src/investments/investment_model.jl | 145 +++++++++++++++++++++++++++ src/investments/requirement_model.jl | 67 +++++++++++++ src/investments/technology_model.jl | 91 +++++++++++++++++ src/investments/transport_model.jl | 13 +++ 4 files changed, 316 insertions(+) create mode 100644 src/investments/investment_model.jl create mode 100644 src/investments/requirement_model.jl create mode 100644 src/investments/technology_model.jl create mode 100644 src/investments/transport_model.jl diff --git a/src/investments/investment_model.jl b/src/investments/investment_model.jl new file mode 100644 index 00000000..17278b58 --- /dev/null +++ b/src/investments/investment_model.jl @@ -0,0 +1,145 @@ +mutable struct InvestmentModel{S <: AbstractOptimizationProblem} <: AbstractOptimizationModel + name::Symbol + template::AbstractProblemTemplate + portfolio::IS.InfrastructureSystemsContainer + internal::Union{Nothing, ModelInternal} + simulation_info::Union{Nothing, SimulationInfo} + store::InvestmentModelStore + ext::Dict{String, Any} +end + +function InvestmentModel{M}( + template::AbstractProblemTemplate, + ::Type{M}, + portfolio::IS.InfrastructureSystemsContainer, + settings::Settings, + jump_model::Union{Nothing, JuMP.Model}=nothing; +) where {M <: AbstractOptimizationProblem} + internal = ModelInternal(OptimizationContainer(portfolio, settings, jump_model)) + + model = InvestmentModel{M}( + :CEM, + template, + portfolio, + internal, + SimulationInfo(), + InvestmentModelStore(), + Dict{String, Any}(), + ) + return model +end + +function InvestmentModel{M}( + template::AbstractProblemTemplate, + portfolio::IS.InfrastructureSystemsContainer, + jump_model::Union{Nothing, JuMP.Model}=nothing; + name=nothing, + optimizer=nothing, + horizon=UNSET_HORIZON, + resolution=UNSET_RESOLUTION, + portfolio_to_file=true, + optimizer_solve_log_print=false, + detailed_optimizer_stats=false, + calculate_conflict=false, + direct_mode_optimizer=false, + store_variable_names=false, + check_numerical_bounds=true, + initial_time=UNSET_INI_TIME, + time_series_cache_size::Int=IS.TIME_SERIES_CACHE_SIZE_BYTES, +) where {M <: AbstractOptimizationProblem} + settings = Settings( + portfolio; + initial_time=initial_time, + time_series_cache_size=time_series_cache_size, + horizon=horizon, + resolution=resolution, + optimizer=optimizer, + direct_mode_optimizer=direct_mode_optimizer, + optimizer_solve_log_print=optimizer_solve_log_print, + detailed_optimizer_stats=detailed_optimizer_stats, + calculate_conflict=calculate_conflict, + system_to_file=portfolio_to_file, + check_numerical_bounds=check_numerical_bounds, + store_variable_names=store_variable_names, + ) + return InvestmentModel{M}(template, M, portfolio, settings, jump_model) +end + +# Default implementations of getter/setter functions for InvestmentModel. +is_built(model::InvestmentModel) = + get_status(get_internal(model)) == ModelBuildStatus.BUILT +isempty(model::InvestmentModel) = + get_status(get_internal(model)) == ModelBuildStatus.EMPTY + +get_constraints(model::InvestmentModel) = + get_constraints(get_internal(model)) +get_internal(model::InvestmentModel) = model.internal + +function get_jump_model(model::InvestmentModel) + return get_jump_model(get_container(get_internal(model))) +end + +get_name(model::InvestmentModel) = model.name +get_store(model::InvestmentModel) = model.store + +function get_optimization_container(model::InvestmentModel) + return get_optimization_container(get_internal(model)) +end + +function get_timestamps(model::InvestmentModel) + optimization_container = get_optimization_container(model) + start_time = get_initial_time(optimization_container) + resolution = get_resolution(model) + horizon_count = get_time_steps(optimization_container)[end] + return range(start_time; length=horizon_count, step=resolution) +end + +# No Base Power for Portfolio models. Always in Natural Units. +get_problem_base_power(model::InvestmentModel) = 1.0 +get_settings(model::InvestmentModel) = get_optimization_container(model).settings +get_optimizer_stats(model::InvestmentModel) = + get_optimizer_stats(get_optimization_container(model)) + +get_status(model::InvestmentModel) = get_status(get_internal(model)) +get_portfolio(model::InvestmentModel) = model.portfolio +get_template(model::InvestmentModel) = model.template +get_time_stamps(model::InvestmentModel) = + get_time_stamps(get_time_mapping(get_optimization_container(model))) + +get_store_params(model::InvestmentModel) = + get_store_params(get_internal(model)) +get_output_dir(model::InvestmentModel) = get_output_dir(get_internal(model)) +get_recorder_dir(model::InvestmentModel) = joinpath(get_output_dir(model), "recorder") + +get_variables(model::InvestmentModel) = get_variables(get_optimization_container(model)) +get_duals(model::InvestmentModel) = get_duals(get_optimization_container(model)) +get_initial_conditions(model::InvestmentModel) = + get_initial_conditions(get_optimization_container(model)) + +get_simulation_info(model::InvestmentModel) = model.simulation_info +get_executions(model::InvestmentModel) = get_executions(get_internal(model)) + +get_run_status(model::InvestmentModel) = get_run_status(get_simulation_info(model)) +set_run_status!(model::InvestmentModel, status) = + set_run_status!(get_simulation_info(model), status) + +get_initial_time(model::InvestmentModel) = get_initial_time(get_settings(model)) +get_resolution(model::InvestmentModel) = get_resolution(get_settings(model)) + +set_console_level!(model::InvestmentModel, val) = + set_console_level!(get_internal(model), val) +set_file_level!(model::InvestmentModel, val) = + set_file_level!(get_internal(model), val) + +function set_status!(model::InvestmentModel, status::ModelBuildStatus) + set_status!(get_internal(model), status) + return +end + +function set_output_dir!(model::InvestmentModel, path::AbstractString) + set_output_dir!(get_internal(model), path) + return +end + +# Portfolio-specific alias for Setttings +get_portfolio_to_file(settings::Settings) = get_system_to_file(settings) \ No newline at end of file diff --git a/src/investments/requirement_model.jl b/src/investments/requirement_model.jl new file mode 100644 index 00000000..b70abe03 --- /dev/null +++ b/src/investments/requirement_model.jl @@ -0,0 +1,67 @@ +""" +Establishes the model for a particular requirement (policy) specified by type and +formulation. Mirrors `ServiceModel` in PowerSimulations.jl and `TechnologyModel` +in this package. + +# Arguments + + - `::Type{D}`: A `PSIP.Requirement` subtype (e.g. `PSIP.EnergyShareRequirements`) + - `::Type{B}`: A `RequirementFormulation` subtype (e.g. `RequirementEnergyShare`) + +# Example + +```julia +requirement = RequirementModel(PSIP.EnergyShareRequirements, RequirementEnergyShare) +``` +""" +mutable struct RequirementModel{D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} + use_slacks::Bool + duals::Vector{DataType} + attributes::Dict{String, Any} +end + +get_requirement_type( + ::RequirementModel{D, B}, +) where {D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} = D + +get_requirement_formulation( + ::RequirementModel{D, B}, +) where {D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} = B + +get_use_slacks(m::RequirementModel) = m.use_slacks +get_duals(m::RequirementModel) = m.duals +get_attributes(m::RequirementModel) = m.attributes + +""" +Default (empty) attributes for a requirement model. Override per +`(requirement_type, formulation)` pair where needed. +""" +get_default_attributes(::Type{<:IS.InfrastructureSystemsComponent}, ::Type{<:RequirementFormulation}) = + Dict{String, Any}() + +function RequirementModel( + ::Type{D}, + ::Type{B}; + use_slacks=false, + duals=Vector{DataType}(), + attributes=Dict{String, Any}(), +) where {D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} + attributes_ = get_default_attributes(D, B) + for (k, v) in attributes + attributes_[k] = v + end + return RequirementModel{D, B}(use_slacks, duals, attributes_) +end + +function _set_model!( + dict::Dict, + names::Vector{String}, + model::RequirementModel{D, B}, +) where {D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} + key = model + if haskey(dict, key) + @warn "Overwriting $(D) existing requirement model" + end + dict[key] = names + return +end diff --git a/src/investments/technology_model.jl b/src/investments/technology_model.jl new file mode 100644 index 00000000..7afe7bcb --- /dev/null +++ b/src/investments/technology_model.jl @@ -0,0 +1,91 @@ +mutable struct TechnologyModel{ + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} + use_slacks::Bool + duals::Vector{DataType} + attributes::Dict{String, Any} +end + +function _set_model!( + dict::Dict, + names::Vector{String}, + model::TechnologyModel{D, A, B, C}, +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} + #key = Symbol(model) + key = model + if haskey(dict, key) + @warn "Overwriting $(D) existing model" + end + dict[key] = names + return +end + +get_technology_type( + ::TechnologyModel{D, A, B, C}, +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} = D + +get_investment_formulation( + ::TechnologyModel{D, A, B, C}, +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} = A + +get_operations_formulation( + ::TechnologyModel{D, A, B, C}, +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} = B + +get_feasibility_formulation( + ::TechnologyModel{D, A, B, C}, +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} = C + +function TechnologyModel( + ::Type{D}, + ::Type{A}, + ::Type{B}, + ::Type{C}; + use_slacks=false, + duals=Vector{DataType}(), + attributes=Dict{String, Any}(), +) where { + D <: IS.InfrastructureSystemsComponent, + A <: InvestmentTechnologyFormulation, + B <: OperationsTechnologyFormulation, + C <: FeasibilityTechnologyFormulation, +} + attributes_ = get_default_attributes(D, A, B, C) + for (k, v) in attributes + attributes_[k] = v + end + + # TODO: Implement check for technologies + #_check_technology_formulation(D, A, B, C) + # TODO: new is only defined for inner constructors, replace for now but we might want to reorganize this file later + #new{D, B, C}(use_slacks, duals, time_series_names, attributes_, nothing) + return TechnologyModel{D, A, B, C}(use_slacks, duals, attributes_) +end diff --git a/src/investments/transport_model.jl b/src/investments/transport_model.jl new file mode 100644 index 00000000..fa2f8f6c --- /dev/null +++ b/src/investments/transport_model.jl @@ -0,0 +1,13 @@ +abstract type AbstractTransportAggregation end + +mutable struct TransportModel{T <: AbstractTransportAggregation} + use_slacks::Bool + function TransportModel( + ::Type{T}; + use_slacks=false, + ) where {T <: AbstractTransportAggregation} + new{T}(use_slacks) + end +end + +get_use_slacks(m::TransportModel) = m.use_slacks \ No newline at end of file From 81bbae9a50f887f1a432abf436fd36d19a8baf8d Mon Sep 17 00:00:00 2001 From: Jerry Potts Date: Tue, 22 Sep 2026 13:23:08 -0400 Subject: [PATCH 3/5] add data to container for investment model --- src/InfrastructureOptimizationModels.jl | 28 +++++ src/core/optimization_container.jl | 92 +++++++++++++++ src/core/settings.jl | 4 + src/core/time_mapping.jl | 137 ++++++++++++++++++++++ src/investments/container_data.jl | 34 ++++++ src/investments/formulations.jl | 9 +- src/investments/investment_model_store.jl | 99 ++++++++++++++++ src/utils/jump_utils.jl | 41 +++++++ 8 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 src/core/time_mapping.jl create mode 100644 src/investments/container_data.jl create mode 100644 src/investments/investment_model_store.jl diff --git a/src/InfrastructureOptimizationModels.jl b/src/InfrastructureOptimizationModels.jl index 116df5d1..6f4127ce 100644 --- a/src/InfrastructureOptimizationModels.jl +++ b/src/InfrastructureOptimizationModels.jl @@ -159,6 +159,7 @@ using DocStringExtensions # Base Models export DecisionModel export EmulationModel +export InvestmentModel, InvestmentModelStore export AbstractProblemTemplate export ServicesModelContainer, DevicesModelContainer, BranchModelContainer export InitialCondition @@ -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 @@ -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 @@ -521,6 +525,10 @@ 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 @@ -537,6 +545,19 @@ 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 @@ -597,6 +618,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") @@ -676,6 +699,11 @@ 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") diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index ca1fb9f6..a11c4b66 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -71,6 +71,7 @@ mutable struct OptimizationContainer <: AbstractOptimizationContainer JuMPmodel::JuMP.Model time_steps::UnitRange{Int} settings::Settings + investment_data::Union{Nothing, InvestmentContainerData} variables::OrderedDict{VariableKey, JuMPArray} aux_variables::OrderedDict{AuxVarKey, JuMPArray} duals::OrderedDict{ConstraintKey, JuMPArray} @@ -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}(), @@ -194,6 +196,60 @@ 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 @@ -371,6 +427,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)) diff --git a/src/core/settings.jl b/src/core/settings.jl index 89d1e15d..89c8e384 100644 --- a/src/core/settings.jl +++ b/src/core/settings.jl @@ -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)) diff --git a/src/core/time_mapping.jl b/src/core/time_mapping.jl new file mode 100644 index 00000000..d4360d1e --- /dev/null +++ b/src/core/time_mapping.jl @@ -0,0 +1,137 @@ +struct InvestmentIntervals + 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 diff --git a/src/investments/container_data.jl b/src/investments/container_data.jl new file mode 100644 index 00000000..13156219 --- /dev/null +++ b/src/investments/container_data.jl @@ -0,0 +1,34 @@ +get_technology_models(template::AbstractProblemTemplate) = template.technology_models +get_branch_models(template::AbstractProblemTemplate) = template.branch_models +get_requirement_models(template::AbstractProblemTemplate) = template.requirement_models +get_transport_model(template::AbstractProblemTemplate) = template.transport_model + +get_capital_model(template::AbstractProblemTemplate) = template.capital_model +get_operation_model(template::AbstractProblemTemplate) = template.operation_model +get_feasibility_model(template::AbstractProblemTemplate) = template.feasibility_model + +""" +Investment-specific data stored in OptimizationContainer's `ext` dictionary. +Contains time mapping, financial parameters, and operational weights. +""" + +# TODO: Move financial parameters out of the container, they can stay in the portfolio +mutable struct InvestmentContainerData + time_mapping::TimeMapping + operational_weights::Union{Nothing, Vector{Float64}} + base_year::Int + discount_rate::Float64 + inflation_rate::Float64 + interest_rate::Float64 +end + +function InvestmentContainerData() + return InvestmentContainerData( + TimeMapping(nothing), + nothing, + 2020, + 0.0, + 0.0, + 0.0, + ) +end \ No newline at end of file diff --git a/src/investments/formulations.jl b/src/investments/formulations.jl index f561d27b..f3db3236 100644 --- a/src/investments/formulations.jl +++ b/src/investments/formulations.jl @@ -1,2 +1,7 @@ -# Should get moved to IS.Optimization and then re-exported by IOM -abstract type AbstractTechnologyFormulation end \ No newline at end of file +# Should get moved to IS.Optimization and then re-exported by IOM? +abstract type AbstractTechnologyFormulation <: AbstractDeviceFormulation end + +abstract type InvestmentTechnologyFormulation <: AbstractTechnologyFormulation end +abstract type OperationsTechnologyFormulation <: AbstractTechnologyFormulation end +abstract type FeasibilityTechnologyFormulation <: AbstractTechnologyFormulation end +abstract type RequirementFormulation <: AbstractServiceFormulation end \ No newline at end of file diff --git a/src/investments/investment_model_store.jl b/src/investments/investment_model_store.jl new file mode 100644 index 00000000..03ff3b48 --- /dev/null +++ b/src/investments/investment_model_store.jl @@ -0,0 +1,99 @@ +mutable struct InvestmentModelStore <: AbstractModelStore + # All DenseAxisArrays have axes (column names, row indexes) + duals::Dict{ConstraintKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}} + variables::Dict{VariableKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}} + aux_variables::Dict{AuxVarKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}} + expressions::Dict{ + ExpressionKey, + OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}, + } + optimizer_stats::OrderedDict{Dates.DateTime, OptimizerStats} +end + +function InvestmentModelStore() + return InvestmentModelStore( + Dict{ConstraintKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}}(), + Dict{VariableKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}}(), + Dict{AuxVarKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}}(), + Dict{ExpressionKey, OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}}(), + OrderedDict{Dates.DateTime, OptimizerStats}(), + ) +end + +function initialize_storage!( + store::InvestmentModelStore, + container::AbstractOptimizationContainer, + params::ModelStoreParams, +) + time_mapping = get_time_mapping(container) + if length(get_time_steps(time_mapping)) < 1 + error("The time step count in the optimization container is not defined") + end + base_timestamp = get_base_date(time_mapping) + op_time_steps_count = get_total_operation_period_count(time_mapping) + cap_time_steps_count = get_total_investment_period_count(time_mapping) + for type in STORE_CONTAINERS + if type == :parameters + continue + end + field_containers = getfield(container, type) + results_container = getfield(store, type) + for (key, field_container) in field_containers + !should_write_resulting_value(get_entry_type(key)) && continue + entry_type = get_entry_type(key) + if is_operation_entry(entry_type) + count = op_time_steps_count + elseif is_investment_entry(entry_type) + count = cap_time_steps_count + else + error() + end + @debug "Adding $(encode_key_as_string(key)) to InvestmentModelStore" _group = + LOG_GROUP_MODEL_STORE + column_names = get_column_names(key, field_container) + data = OrderedDict{Dates.DateTime, DenseAxisArray{Float64, 2}}() + data[base_timestamp] = + fill!(DenseAxisArray{Float64}(undef, column_names..., 1:count), NaN) + results_container[key] = data + end + end + return +end + +function write_optimizer_stats!( + store::InvestmentModelStore, + stats::OptimizerStats, + index::Dates.Date, +) + if index in keys(store.optimizer_stats) + @warn "Overwriting optimizer stats" + end + store.optimizer_stats[index] = stats + return +end + +function read_optimizer_stats(store::InvestmentModelStore) + stats = [IS.to_namedtuple(x) for x in values(store.optimizer_stats)] + df = DataFrames.DataFrame(stats) + DataFrames.insertcols!(df, 1, :DateTime => keys(store.optimizer_stats)) + return df +end + +function get_column_names(store::InvestmentModelStore, key::OptimizationContainerKey) + container = getfield(store, get_store_container_type(key)) + return get_column_names(key, first(values(container[key]))) +end + +is_operation_entry(::Type{<:OperationsExpressionType}) = true +is_operation_entry(::Type{<:InvestmentExpressionType}) = false + +is_investment_entry(::Type{<:OperationsExpressionType}) = false +is_investment_entry(::Type{<:InvestmentExpressionType}) = true + +is_operation_entry(::Type{<:VariableType}) = error() +is_operation_entry(::Type{<:OperationsVariableType}) = true +is_operation_entry(::Type{<:InvestmentVariableType}) = false + +is_investment_entry(::Type{<:VariableType}) = error() +is_investment_entry(::Type{<:OperationsVariableType}) = false +is_investment_entry(::Type{<:InvestmentVariableType}) = true \ No newline at end of file diff --git a/src/utils/jump_utils.jl b/src/utils/jump_utils.jl index fca14cb7..6f27b378 100644 --- a/src/utils/jump_utils.jl +++ b/src/utils/jump_utils.jl @@ -247,6 +247,47 @@ function to_dataframe( ) end +function get_column_names( + key::OptimizationContainerKey, + ::DenseAxisArray{T, 1, K}, +) where {T, K <: NTuple{1, Any}} + return get_column_names(key) +end + +function get_column_names( + k::OptimizationContainerKey, + array::DenseAxisArray{T, 2, K}, +) where {T, K <: NTuple{2, Any}} + return (string.(axes(array)[1]),) +end + +function get_column_names( + k::OptimizationContainerKey, + array::DenseAxisArray{T, 3, K}, +) where {T, K <: NTuple{3, Any}} + return (string.(axes(array)[1]), string.(axes(array)[2])) +end + +function _get_column_names(arr::SparseAxisArray{T, N, K}) where {T, N, K <: NTuple{N, Any}} + return sort!(collect(Set(encode_tuple_to_column(k[1:(N - 1)]) for k in keys(arr.data)))) +end + +function get_column_names( + ::OptimizationContainerKey, + array::SparseAxisArray{T, N, K}, +) where {T, N, K <: NTuple{N, Any}} + return (get_column_names(array),) +end + +function get_column_names(array::SparseAxisArray{T, N, K}) where {T, N, K <: NTuple{N, Any}} + return _get_column_names(array) +end + +function to_dataframe(array::SparseAxisArray{T, N, K}) where {T, N, K <: NTuple{N, Any}} + columns = _get_column_names(array) + return DataFrames.DataFrame(_to_matrix(array, columns), columns) +end + """ Convert a DenseAxisArray containing components to a outputs DataFrame consumable by users. From 50df44374ccb34c3e9627c75b18e97081316a01b Mon Sep 17 00:00:00 2001 From: Jerry Potts Date: Tue, 22 Sep 2026 13:26:47 -0400 Subject: [PATCH 4/5] formatter --- src/InfrastructureOptimizationModels.jl | 6 ++- src/core/optimization_container.jl | 3 +- src/core/standard_variables_expressions.jl | 2 +- src/core/time_mapping.jl | 4 +- src/investments/container_data.jl | 2 +- src/investments/formulations.jl | 2 +- src/investments/investment_model.jl | 61 +++++++++++----------- src/investments/investment_model_store.jl | 2 +- src/investments/requirement_model.jl | 16 ++++-- src/investments/technology_model.jl | 6 +-- src/investments/transport_model.jl | 4 +- 11 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/InfrastructureOptimizationModels.jl b/src/InfrastructureOptimizationModels.jl index 6f4127ce..0eb9f95f 100644 --- a/src/InfrastructureOptimizationModels.jl +++ b/src/InfrastructureOptimizationModels.jl @@ -526,7 +526,8 @@ export SimulationBuildStatus export AbstractOptimizationProblem # Technology and Requirement Formulations -export InvestmentTechnologyFormulation, OperationsTechnologyFormulation, FeasibilityTechnologyFormulation +export InvestmentTechnologyFormulation, + OperationsTechnologyFormulation, FeasibilityTechnologyFormulation export RequirementFormulation # Settings and Data Types @@ -552,7 +553,8 @@ 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 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 diff --git a/src/core/optimization_container.jl b/src/core/optimization_container.jl index a11c4b66..098eb1e2 100644 --- a/src/core/optimization_container.jl +++ b/src/core/optimization_container.jl @@ -249,7 +249,6 @@ function set_interest_rate!(container::OptimizationContainer, interest_rate::Flo return end - function set_serialization_task!(container::OptimizationContainer, task::Task) container.serialization_task = task return @@ -430,7 +429,7 @@ end function init_optimization_container!( container::OptimizationContainer, template::AbstractProblemTemplate, - portfolio::IS.InfrastructureSystemsContainer + portfolio::IS.InfrastructureSystemsContainer, ) # The order of operations matter transport_model = get_transport_model(template) diff --git a/src/core/standard_variables_expressions.jl b/src/core/standard_variables_expressions.jl index 170479e8..4e823f56 100644 --- a/src/core/standard_variables_expressions.jl +++ b/src/core/standard_variables_expressions.jl @@ -165,4 +165,4 @@ abstract type FeasibilityVariableType <: VariableType end ### Investment Variables ### -abstract type BuildInvestmentVariableType <: InvestmentVariableType end \ No newline at end of file +abstract type BuildInvestmentVariableType <: InvestmentVariableType end diff --git a/src/core/time_mapping.jl b/src/core/time_mapping.jl index d4360d1e..3b1fef67 100644 --- a/src/core/time_mapping.jl +++ b/src/core/time_mapping.jl @@ -78,7 +78,7 @@ function TimeMapping( error() end slice_length = length(slice) - slice_indeces = range(slice_running_count + 1, length=slice_length) + 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 @@ -91,7 +91,7 @@ function TimeMapping( time_stamps, consecutive_slices, inverse_invest_mapping, - collect(range(start=op_index_last_slice + 1, stop=total_slice_count)), + collect(range(; start = op_index_last_slice + 1, stop = total_slice_count)), collect(range(1, op_index_last_slice)), ) diff --git a/src/investments/container_data.jl b/src/investments/container_data.jl index 13156219..653a3cb9 100644 --- a/src/investments/container_data.jl +++ b/src/investments/container_data.jl @@ -31,4 +31,4 @@ function InvestmentContainerData() 0.0, 0.0, ) -end \ No newline at end of file +end diff --git a/src/investments/formulations.jl b/src/investments/formulations.jl index f3db3236..4419ed8c 100644 --- a/src/investments/formulations.jl +++ b/src/investments/formulations.jl @@ -4,4 +4,4 @@ abstract type AbstractTechnologyFormulation <: AbstractDeviceFormulation end abstract type InvestmentTechnologyFormulation <: AbstractTechnologyFormulation end abstract type OperationsTechnologyFormulation <: AbstractTechnologyFormulation end abstract type FeasibilityTechnologyFormulation <: AbstractTechnologyFormulation end -abstract type RequirementFormulation <: AbstractServiceFormulation end \ No newline at end of file +abstract type RequirementFormulation <: AbstractServiceFormulation end diff --git a/src/investments/investment_model.jl b/src/investments/investment_model.jl index 17278b58..471fa4ad 100644 --- a/src/investments/investment_model.jl +++ b/src/investments/investment_model.jl @@ -1,4 +1,5 @@ -mutable struct InvestmentModel{S <: AbstractOptimizationProblem} <: AbstractOptimizationModel +mutable struct InvestmentModel{S <: AbstractOptimizationProblem} <: + AbstractOptimizationModel name::Symbol template::AbstractProblemTemplate portfolio::IS.InfrastructureSystemsContainer @@ -13,7 +14,7 @@ function InvestmentModel{M}( ::Type{M}, portfolio::IS.InfrastructureSystemsContainer, settings::Settings, - jump_model::Union{Nothing, JuMP.Model}=nothing; + jump_model::Union{Nothing, JuMP.Model} = nothing; ) where {M <: AbstractOptimizationProblem} internal = ModelInternal(OptimizationContainer(portfolio, settings, jump_model)) @@ -32,35 +33,35 @@ end function InvestmentModel{M}( template::AbstractProblemTemplate, portfolio::IS.InfrastructureSystemsContainer, - jump_model::Union{Nothing, JuMP.Model}=nothing; - name=nothing, - optimizer=nothing, - horizon=UNSET_HORIZON, - resolution=UNSET_RESOLUTION, - portfolio_to_file=true, - optimizer_solve_log_print=false, - detailed_optimizer_stats=false, - calculate_conflict=false, - direct_mode_optimizer=false, - store_variable_names=false, - check_numerical_bounds=true, - initial_time=UNSET_INI_TIME, - time_series_cache_size::Int=IS.TIME_SERIES_CACHE_SIZE_BYTES, + jump_model::Union{Nothing, JuMP.Model} = nothing; + name = nothing, + optimizer = nothing, + horizon = UNSET_HORIZON, + resolution = UNSET_RESOLUTION, + portfolio_to_file = true, + optimizer_solve_log_print = false, + detailed_optimizer_stats = false, + calculate_conflict = false, + direct_mode_optimizer = false, + store_variable_names = false, + check_numerical_bounds = true, + initial_time = UNSET_INI_TIME, + time_series_cache_size::Int = IS.TIME_SERIES_CACHE_SIZE_BYTES, ) where {M <: AbstractOptimizationProblem} settings = Settings( portfolio; - initial_time=initial_time, - time_series_cache_size=time_series_cache_size, - horizon=horizon, - resolution=resolution, - optimizer=optimizer, - direct_mode_optimizer=direct_mode_optimizer, - optimizer_solve_log_print=optimizer_solve_log_print, - detailed_optimizer_stats=detailed_optimizer_stats, - calculate_conflict=calculate_conflict, - system_to_file=portfolio_to_file, - check_numerical_bounds=check_numerical_bounds, - store_variable_names=store_variable_names, + initial_time = initial_time, + time_series_cache_size = time_series_cache_size, + horizon = horizon, + resolution = resolution, + optimizer = optimizer, + direct_mode_optimizer = direct_mode_optimizer, + optimizer_solve_log_print = optimizer_solve_log_print, + detailed_optimizer_stats = detailed_optimizer_stats, + calculate_conflict = calculate_conflict, + system_to_file = portfolio_to_file, + check_numerical_bounds = check_numerical_bounds, + store_variable_names = store_variable_names, ) return InvestmentModel{M}(template, M, portfolio, settings, jump_model) end @@ -91,7 +92,7 @@ function get_timestamps(model::InvestmentModel) start_time = get_initial_time(optimization_container) resolution = get_resolution(model) horizon_count = get_time_steps(optimization_container)[end] - return range(start_time; length=horizon_count, step=resolution) + return range(start_time; length = horizon_count, step = resolution) end # No Base Power for Portfolio models. Always in Natural Units. @@ -142,4 +143,4 @@ function set_output_dir!(model::InvestmentModel, path::AbstractString) end # Portfolio-specific alias for Setttings -get_portfolio_to_file(settings::Settings) = get_system_to_file(settings) \ No newline at end of file +get_portfolio_to_file(settings::Settings) = get_system_to_file(settings) diff --git a/src/investments/investment_model_store.jl b/src/investments/investment_model_store.jl index 03ff3b48..0023fdaa 100644 --- a/src/investments/investment_model_store.jl +++ b/src/investments/investment_model_store.jl @@ -96,4 +96,4 @@ is_operation_entry(::Type{<:InvestmentVariableType}) = false is_investment_entry(::Type{<:VariableType}) = error() is_investment_entry(::Type{<:OperationsVariableType}) = false -is_investment_entry(::Type{<:InvestmentVariableType}) = true \ No newline at end of file +is_investment_entry(::Type{<:InvestmentVariableType}) = true diff --git a/src/investments/requirement_model.jl b/src/investments/requirement_model.jl index b70abe03..1110588f 100644 --- a/src/investments/requirement_model.jl +++ b/src/investments/requirement_model.jl @@ -14,7 +14,10 @@ in this package. requirement = RequirementModel(PSIP.EnergyShareRequirements, RequirementEnergyShare) ``` """ -mutable struct RequirementModel{D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} +mutable struct RequirementModel{ + D <: IS.InfrastructureSystemsComponent, + B <: RequirementFormulation, +} use_slacks::Bool duals::Vector{DataType} attributes::Dict{String, Any} @@ -36,15 +39,18 @@ get_attributes(m::RequirementModel) = m.attributes Default (empty) attributes for a requirement model. Override per `(requirement_type, formulation)` pair where needed. """ -get_default_attributes(::Type{<:IS.InfrastructureSystemsComponent}, ::Type{<:RequirementFormulation}) = +get_default_attributes( + ::Type{<:IS.InfrastructureSystemsComponent}, + ::Type{<:RequirementFormulation}, +) = Dict{String, Any}() function RequirementModel( ::Type{D}, ::Type{B}; - use_slacks=false, - duals=Vector{DataType}(), - attributes=Dict{String, Any}(), + use_slacks = false, + duals = Vector{DataType}(), + attributes = Dict{String, Any}(), ) where {D <: IS.InfrastructureSystemsComponent, B <: RequirementFormulation} attributes_ = get_default_attributes(D, B) for (k, v) in attributes diff --git a/src/investments/technology_model.jl b/src/investments/technology_model.jl index 7afe7bcb..fd7547a4 100644 --- a/src/investments/technology_model.jl +++ b/src/investments/technology_model.jl @@ -69,9 +69,9 @@ function TechnologyModel( ::Type{A}, ::Type{B}, ::Type{C}; - use_slacks=false, - duals=Vector{DataType}(), - attributes=Dict{String, Any}(), + use_slacks = false, + duals = Vector{DataType}(), + attributes = Dict{String, Any}(), ) where { D <: IS.InfrastructureSystemsComponent, A <: InvestmentTechnologyFormulation, diff --git a/src/investments/transport_model.jl b/src/investments/transport_model.jl index fa2f8f6c..bf76d160 100644 --- a/src/investments/transport_model.jl +++ b/src/investments/transport_model.jl @@ -4,10 +4,10 @@ mutable struct TransportModel{T <: AbstractTransportAggregation} use_slacks::Bool function TransportModel( ::Type{T}; - use_slacks=false, + use_slacks = false, ) where {T <: AbstractTransportAggregation} new{T}(use_slacks) end end -get_use_slacks(m::TransportModel) = m.use_slacks \ No newline at end of file +get_use_slacks(m::TransportModel) = m.use_slacks From dd86e2af5c8afd15fded13ab42a334621fae2b33 Mon Sep 17 00:00:00 2001 From: Jerry Potts Date: Tue, 22 Sep 2026 15:00:17 -0400 Subject: [PATCH 5/5] fix enum --- src/investments/investment_model.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/investments/investment_model.jl b/src/investments/investment_model.jl index 471fa4ad..4569289e 100644 --- a/src/investments/investment_model.jl +++ b/src/investments/investment_model.jl @@ -132,7 +132,7 @@ set_console_level!(model::InvestmentModel, val) = set_file_level!(model::InvestmentModel, val) = set_file_level!(get_internal(model), val) -function set_status!(model::InvestmentModel, status::ModelBuildStatus) +function set_status!(model::InvestmentModel, status::ModelBuildStatus.Value) set_status!(get_internal(model), status) return end