diff --git a/scripts/portfolio_test.jl b/scripts/portfolio_test.jl index f8c49ff..83691b9 100644 --- a/scripts/portfolio_test.jl +++ b/scripts/portfolio_test.jl @@ -9,7 +9,7 @@ const IS = InfrastructureSystems const PSY = PowerSystems const PSIP = PowerSystemsInvestmentsPortfolios -data = PSY._create_system_data_from_kwargs() +#data = PSY._create_system_data_from_kwargs() #bus = ACBus(nothing) #discount_rate = 0.07 @@ -29,7 +29,9 @@ t_th = SupplyTechnology{ThermalStandard}( ) t_th_ext = t_th.ext -t_th_ext["capital_cost"] = 50.0 +t_th_ext["capital_cost"] = [50.0, 125.0] +t_th_ext["operations_cost"] = [5.0, 2.5] +t_th_ext["variable_cost"] = [1.0, 1.0] t_th_ext["investment_periods"] = [2030, 2040] # one option is to repeat these for each investment t_th_ext["operational_periods"] = [1, 2, 3] @@ -38,8 +40,12 @@ t_th_ext["operational_periods_2"] = [ 4, 5, 6 # second investment period ] t_th_ext["investment_operational_periods_map"] = Dict( - 2030 => [1, 2, 3], - 2040 => [4, 5, 6], + 1 => 2030, + 2 => 2030, + 3 => 2030, + 4 => 2040, + 5 => 2040, + 6 => 2040, ) t_th_ext["initial_capacity"] = 200.0 # Data in MW t_th_ext["maximum_capacity"] = 10000.0 @@ -56,9 +62,30 @@ t_th_exp = SupplyTechnology{ThermalStandard}( ) t_th_exp_ext = t_th_exp.ext -t_th_exp_ext["capital_cost"] = 150.0 +t_th_exp_ext["capital_cost"] = [150.0, 100.0] +t_th_exp_ext["operations_cost"] = [15.0, 10.0] +t_th_exp_ext["variable_cost"] = [1.0, 1.0] +t_th_exp_ext["investment_periods"] = [2030, 2040] +# one option is to repeat these for each investment +t_th_exp_ext["operational_periods"] = [1, 2, 3] +t_th_exp_ext["operational_periods_2"] = [ + 1, 2, 3, # first investment period + 4, 5, 6 # second investment period +] +t_th_exp_ext["investment_operational_periods_map"] = Dict( + 1 => 2030, + 2 => 2030, + 3 => 2030, + 4 => 2040, + 5 => 2040, + 6 => 2040, +) +t_th_exp_ext["initial_capacity"] = 200.0 # Data in MW +t_th_exp_ext["maximum_capacity"] = 10000.0 +t_th_exp_ext["minimum_required_capacity"] = [0.0, 0.0] + -# Initial Capacity +# Renewable Technology t_re = SupplyTechnology{RenewableDispatch}( name="renewable_tech", @@ -71,21 +98,54 @@ t_re = SupplyTechnology{RenewableDispatch}( ) t_re_ext = t_re.ext -t_re_ext["capital_cost"] = 100.0 -t_re_ext["variable_capacity_factor"] = [0.8, 0.6, 0.3] +t_re_ext["capital_cost"] = [100.0, 75.0] +t_re_ext["operations_cost"] = [10.0, 6.5] +t_re_ext["variable_cost"] = [0, 0] +t_re_ext["variable_capacity_factor"] = [0.8, 0.6, 0.3, 0.5, 0.9, 0.4] +t_re_ext["investment_periods"] = [2030, 2040] +# one option is to repeat these for each investment +t_re_ext["operational_periods"] = [1, 2, 3] +t_re_ext["operational_periods_2"] = [ + 1, 2, 3, # first investment period + 4, 5, 6 # second investment period +] +t_re_ext["investment_operational_periods_map"] = Dict( + 1 => 2030, + 2 => 2030, + 3 => 2030, + 4 => 2040, + 5 => 2040, + 6 => 2040, +) +t_re_ext["initial_capacity"] = 200.0 # Data in MW +t_re_ext["maximum_capacity"] = 10000.0 +t_re_ext["minimum_required_capacity"] = [0.0, 0.0] + + +# Demand side technologies +d_1 = DemandSideTechnology{ElectricLoad}( + name="demand", + available=true, + capital_cost +) PSIP.add_technology!(p, t_th) PSIP.add_technology!(p, t_re) +PSIP.add_technology!(p, t_th_exp) -IS.serialize(t) +IS.serialize(t_th) +IS.serialize(t_re) +IS.serialize(t_th_exp) IS.serialize(p) -get_technologies(x -> (!get_available(x)), SupplyTechnology{ThermalStandard}, p) +#get_technologies(x -> (!get_available(x)), SupplyTechnology{ThermalStandard}, p) -get_technologies(SupplyTechnology{ThermalStandard}, p) -PSIP.remove_technology!(SupplyTechnology{ThermalStandard}, p, "thermal_tech") +#get_technologies(SupplyTechnology{ThermalStandard}, p) +#PSIP.remove_technology!(SupplyTechnology{ThermalStandard}, p, "thermal_tech") -get_available(t) -IS.deserialize(p) +#get_available(t_th) +#get_available(t_re) +#get_available(t_th_exp) +#IS.deserialize(p) diff --git a/scripts/simple_cem.jl b/scripts/simple_cem.jl new file mode 100644 index 0000000..8c75821 --- /dev/null +++ b/scripts/simple_cem.jl @@ -0,0 +1,243 @@ +using HiGHS +using JuMP + +abstract type GenTech end +abstract type InvestmentModel end +abstract type OperationModel end +abstract type RegionalModel end +abstract type OptimizationModel end +#mutable struct ThermalGen <: GenTech end + +# Include simplified portfolio +include("portfolio_test.jl") + +#struct to define basic capacity expansion model, broken down into operation and InvestmentModel +struct SimpleInvest <: InvestmentModel end +struct SimpleOp <: OperationModel end + +#Defining Model types +inv = SimpleInvest() +op = SimpleOp() + +# Defining empty dictionaries for constraints, expressions, variables, etc. How is this done in Sienna? + +# investment definitions +build = Dict() +capacity = Dict() +build_nonneg = Dict() +cap_bounds = Dict() +capital_costs = Dict() +fom_costs = Dict() + +# operations definitions +dispatch = Dict() +dispatch_lb = Dict() +dispatch_ub = Dict() +dispatch_limit = Dict() +demand_matching = Dict() +vom_costs = Dict() + +################## +# Investment variables +################## + +function add_variables!(model, T::SupplyTechnology{ThermalStandard}, U::SimpleInvest) + print("Thermal investment variables called \n") + for t in T.ext["investment_periods"] + + i = findfirst(==(t), T.ext["investment_periods"]) + + # Build and capacity variables + build[T.name, t] = JuMP.@variable(model) + capacity[T.name, t] = JuMP.@expression(model, (T.ext["initial_capacity"] + sum( build[T.name,t_p] for t_p in T.ext["investment_periods"] if t_p <= t))) + + # Investment and FOM cost calculations + capital_costs[T.name, t] = JuMP.@expression(model, T.ext["capital_cost"][i]*build[T.name, t]) + fom_costs[T.name, t] = JuMP.@expression(model, T.ext["operations_cost"][i]*capacity[T.name, t]) + + end + +end + +function add_variables!(model, T::SupplyTechnology{RenewableDispatch}, U::SimpleInvest) + print("Renewable investment variables called \n") + for t in T.ext["investment_periods"] + + i = findfirst(==(t), T.ext["investment_periods"]) + + # Build and capacity variables + build[T.name, t] = JuMP.@variable(model) + capacity[T.name, t] = JuMP.@expression(model, (T.ext["initial_capacity"] + sum( build[T.name,t_p] for t_p in T.ext["investment_periods"] if t_p <= t))) + + # Investment and FOM cost calculations + capital_costs[T.name, t] = JuMP.@expression(model, T.ext["capital_cost"][i]*build[T.name, t]) + fom_costs[T.name, t] = JuMP.@expression(model, T.ext["operations_cost"][i]*capacity[T.name, t]) + + end + +end + +############ +# Operations variables +############ + +function add_variables!(model, T::SupplyTechnology{ThermalStandard}, U::SimpleOp) + print("Thermal operation variable called \n") + for t in T.ext["operational_periods_2"] + + i = findfirst(==(t), T.ext["operational_periods_2"]) + + i_mapper = findfirst(==(t_th.ext["investment_operational_periods_map"][t]), t_th.ext["investment_periods"]) + + #Dispatch Variable + dispatch[T.name, t] = JuMP.@variable(model) + + #VOM costs + vom_costs[T.name, t] = JuMP.@expression(model, dispatch[T.name, t]*T.ext["variable_cost"][i_mapper]) + + end + +end + +function add_variables!(model, T::SupplyTechnology{RenewableDispatch}, U::SimpleOp) + print("Renewable operation variable called\n") + for t in T.ext["operational_periods_2"] + + i = findfirst(==(t), T.ext["operational_periods_2"]) + + i_mapper = findfirst(==(t_th.ext["investment_operational_periods_map"][t]), t_th.ext["investment_periods"]) + + #Dispatch Variable + dispatch[T.name, t] = JuMP.@variable(model) + + #VOM costs + vom_costs[T.name, t] = JuMP.@expression(model, dispatch[T.name, t]*T.ext["variable_cost"][i_mapper]) + + end + +end + +############ +# Investment constraints +############ + +function add_constraints!(model, T::SupplyTechnology{ThermalStandard}, U::SimpleInvest) + print("Thermal investment constraints called\n") + for t in T.ext["investment_periods"] + + i = findfirst(==(t), T.ext["investment_periods"]) + + # Build and capacity variable bounds + build_nonneg[T.name, t] = JuMP.@constraint(model, 0 <= build[T.name, t]) + cap_bounds[T.name, t] = JuMP.@constraint(model, T.ext["minimum_required_capacity"][i] <= capacity[T.name, t] <= T.ext["maximum_capacity"]) + + end + +end + +function add_constraints!(model, T::SupplyTechnology{RenewableDispatch}, U::SimpleInvest) + print("Renewable investment constraints called\n") + for t in T.ext["investment_periods"] + + i = findfirst(==(t), T.ext["investment_periods"]) + + # Build and capacity variable bounds + build_nonneg[T.name, t] = JuMP.@constraint(model, 0 <= build[T.name, t]) + cap_bounds[T.name, t] = JuMP.@constraint(model, T.ext["minimum_required_capacity"][i] <= capacity[T.name, t] <= T.ext["maximum_capacity"]) + + end + +end + +########### +# Operations Constraints +########### + +function add_constraints!(model, T::SupplyTechnology{ThermalStandard}, U::SimpleOp) + print("Thermal operations constraints called\n") + for t in T.ext["operational_periods_2"] + + i = findfirst(==(t), T.ext["operational_periods_2"]) + + i_mapper = findfirst(==(t_th.ext["investment_operational_periods_map"][t]), t_th.ext["investment_periods"]) + + # Dispatch Variable + dispatch_lb[T.name, t] = JuMP.@constraint(model, 0 <= dispatch[T.name, t]) + dispatch_ub[T.name, t] = JuMP.@constraint(model, dispatch[T.name, t] <= + T.capacity_factor*capacity[T.name, T.ext["investment_operational_periods_map"][t]]) + + end + +end + +function add_constraints!(model, T::SupplyTechnology{RenewableDispatch}, U::SimpleOp) + print("Renewable operations constraints called\n") + for t in T.ext["operational_periods_2"] + + i = findfirst(==(t), T.ext["operational_periods_2"]) + + i_mapper = findfirst(==(t_th.ext["investment_operational_periods_map"][t]), t_th.ext["investment_periods"]) + + # Dispatch Variable + dispatch_lb[T.name, t] = JuMP.@constraint(model, 0 <= dispatch[T.name, t]) + dispatch_ub[T.name, t] = JuMP.@constraint(model, dispatch[T.name, t] <= + T.ext["variable_capacity_factor"][i]*T.capacity_factor*capacity[T.name, T.ext["investment_operational_periods_map"][t]]) + + end + +end + +function add_constraints!(model, U::SimpleOp) + print("system operations constraints called\n") + for t in T_o + + demand_matching[t] = JuMP.@constraint(model, loads[t] <= sum(dispatch[g,t] for g in gens)) + + end + +end + +############# +# Objective function +############# + +function add_objective_function!(model) + JuMP.@objective(model, Min, sum(capital_costs[g, t]+fom_costs[g, t] for g in gens for t in T_i)+weights[t]*sum(vom_costs[g, t] for g in gens for t in T_o)) +end + +################## +# Running the optimization model +################## + +#defining toy data for writing model +loads = Dict(1 => 100, 2 => 150, 3 => 175, 4 => 80, 5=>300, 6=>120) +weights = Dict(1 => 12, 2 => 12, 3 => 12, 4 => 12, 5=>12, 6=>12) + +# initialize model +model = JuMP.Model(HiGHS.Optimizer) + +# Calling investment variables +add_variables!(model, t_th, inv) +add_variables!(model, t_th_exp, inv) +add_variables!(model, t_re, inv) + +# Calling investment constraints +add_constraints!(model, t_th, inv) +add_constraints!(model, t_th_exp, inv) +add_constraints!(model, t_re, inv) + +# Calling operation variable definitions +add_variables!(model, t_th, op) +add_variables!(model, t_th_exp, op) +add_variables!(model, t_re, op) + +# Calling operation constraint definitions +add_constraints!(model, t_th, op) +add_constraints!(model, t_th_exp, op) +add_constraints!(model, t_re, op) + +# Calling demand matching constraints +add_constraints!(model, op) + +# Calling objective function +add_objective_function!(model) \ No newline at end of file