Implement initial plant capacity

This commit is contained in:
2023-02-23 10:34:15 -06:00
parent 1f3a3c9317
commit 256b863c34
11 changed files with 138 additions and 95 deletions

View File

@@ -76,14 +76,14 @@ function parse(json)::Instance
prod_centers = []
product = Product(
product_name,
cost,
energy,
emissions,
disposal_limit,
disposal_cost,
acquisition_cost,
prod_centers,
acquisition_cost = acquisition_cost,
collection_centers = prod_centers,
disposal_cost = disposal_cost,
disposal_limit = disposal_limit,
name = product_name,
transportation_cost = cost,
transportation_emissions = emissions,
transportation_energy = energy,
)
push!(products, product)
prod_name_to_product[product_name] = product
@@ -97,12 +97,12 @@ function parse(json)::Instance
center_dict["longitude (deg)"] = region.centroid.lon
end
center = CollectionCenter(
length(collection_centers) + 1,
center_name,
center_dict["latitude (deg)"],
center_dict["longitude (deg)"],
product,
center_dict["amount (tonne)"],
amount = center_dict["amount (tonne)"],
index = length(collection_centers) + 1,
latitude = center_dict["latitude (deg)"],
longitude = center_dict["longitude (deg)"],
name = center_name,
product = product,
)
push!(prod_centers, center)
push!(collection_centers, center)
@@ -164,16 +164,22 @@ function parse(json)::Instance
push!(
sizes,
PlantSize(
Base.parse(Float64, capacity_name),
capacity_dict["variable operating cost (\$/tonne)"],
capacity_dict["fixed operating cost (\$)"],
capacity_dict["opening cost (\$)"],
capacity = Base.parse(Float64, capacity_name),
fixed_operating_cost = capacity_dict["fixed operating cost (\$)"],
opening_cost = capacity_dict["opening cost (\$)"],
variable_operating_cost = capacity_dict["variable operating cost (\$/tonne)"],
),
)
end
length(sizes) > 1 || push!(sizes, sizes[1])
sort!(sizes, by = x -> x.capacity)
# Initial capacity
initial_capacity = 0
if "initial capacity (tonne)" in keys(location_dict)
initial_capacity = location_dict["initial capacity (tonne)"]
end
# Storage
storage_limit = 0
storage_cost = zeros(T)
@@ -192,20 +198,21 @@ function parse(json)::Instance
end
plant = Plant(
length(plants) + 1,
plant_name,
location_name,
input,
output,
location_dict["latitude (deg)"],
location_dict["longitude (deg)"],
disposal_limit,
disposal_cost,
sizes,
energy,
emissions,
storage_limit,
storage_cost,
disposal_cost = disposal_cost,
disposal_limit = disposal_limit,
emissions = emissions,
energy = energy,
index = length(plants) + 1,
initial_capacity = initial_capacity,
input = input,
latitude = location_dict["latitude (deg)"],
location_name = location_name,
longitude = location_dict["longitude (deg)"],
output = output,
plant_name = plant_name,
sizes = sizes,
storage_cost = storage_cost,
storage_limit = storage_limit,
)
push!(plants, plant)
@@ -216,11 +223,11 @@ function parse(json)::Instance
@info @sprintf("%12d candidate plant locations", length(plants))
return Instance(
T,
products,
collection_centers,
plants,
building_period,
distance_metric,
time = T,
products = products,
collection_centers = collection_centers,
plants = plants,
building_period = building_period,
distance_metric = distance_metric,
)
end

View File

@@ -8,48 +8,49 @@ using JSONSchema
using Printf
using Statistics
mutable struct Product
name::String
transportation_cost::Vector{Float64}
transportation_energy::Vector{Float64}
transportation_emissions::Dict{String,Vector{Float64}}
disposal_limit::Vector{Float64}
disposal_cost::Vector{Float64}
Base.@kwdef mutable struct Product
acquisition_cost::Vector{Float64}
collection_centers::Vector
disposal_cost::Vector{Float64}
disposal_limit::Vector{Float64}
name::String
transportation_cost::Vector{Float64}
transportation_emissions::Dict{String,Vector{Float64}}
transportation_energy::Vector{Float64}
end
mutable struct CollectionCenter
Base.@kwdef mutable struct CollectionCenter
amount::Vector{Float64}
index::Int64
name::String
latitude::Float64
longitude::Float64
name::String
product::Product
amount::Vector{Float64}
end
mutable struct PlantSize
Base.@kwdef mutable struct PlantSize
capacity::Float64
variable_operating_cost::Vector{Float64}
fixed_operating_cost::Vector{Float64}
opening_cost::Vector{Float64}
variable_operating_cost::Vector{Float64}
end
mutable struct Plant
index::Int64
plant_name::String
location_name::String
input::Product
output::Dict{Product,Float64}
latitude::Float64
longitude::Float64
disposal_limit::Dict{Product,Vector{Float64}}
Base.@kwdef mutable struct Plant
disposal_cost::Dict{Product,Vector{Float64}}
sizes::Vector{PlantSize}
energy::Vector{Float64}
disposal_limit::Dict{Product,Vector{Float64}}
emissions::Dict{String,Vector{Float64}}
storage_limit::Float64
energy::Vector{Float64}
index::Int64
initial_capacity::Float64
input::Product
latitude::Float64
location_name::String
longitude::Float64
output::Dict{Product,Float64}
plant_name::String
sizes::Vector{PlantSize}
storage_cost::Vector{Float64}
storage_limit::Float64
end
@@ -62,11 +63,11 @@ end
mutable struct EuclideanDistance <: DistanceMetric end
mutable struct Instance
time::Int64
products::Vector{Product}
collection_centers::Vector{CollectionCenter}
plants::Vector{Plant}
Base.@kwdef mutable struct Instance
building_period::Vector{Int64}
collection_centers::Vector{CollectionCenter}
distance_metric::DistanceMetric
plants::Vector{Plant}
products::Vector{Product}
time::Int64
end

View File

@@ -44,7 +44,7 @@ function create_vars!(model::JuMP.Model)
(n, t) => @variable(model, binary = true) for n in values(graph.process_nodes),
t = 1:T
)
model[:is_open] = Dict(
model[:is_open] = Dict{Tuple,Any}(
(n, t) => @variable(model, binary = true) for n in values(graph.process_nodes),
t = 1:T
)
@@ -55,13 +55,21 @@ function create_vars!(model::JuMP.Model)
upper_bound = n.location.sizes[2].capacity
) for n in values(graph.process_nodes), t = 1:T
)
model[:expansion] = Dict(
model[:expansion] = Dict{Tuple,Any}(
(n, t) => @variable(
model,
lower_bound = 0,
upper_bound = n.location.sizes[2].capacity - n.location.sizes[1].capacity
) for n in values(graph.process_nodes), t = 1:T
)
# Boundary constants
for n in values(graph.process_nodes)
m_init = n.location.initial_capacity
m_min = n.location.sizes[1].capacity
model[:is_open][n, 0] = m_init == 0 ? 0 : 1
model[:expansion][n, 0] = max(0, m_init - m_min)
end
end
@@ -132,6 +140,7 @@ function create_objective_function!(model::JuMP.Model)
)
else
add_to_expression!(obj, slope_open(n.location, t), model[:expansion][n, t])
add_to_expression!(obj, -slope_open(n.location, 1) * model[:expansion][n, 0])
end
end
@@ -244,11 +253,11 @@ function create_process_node_constraints!(model::JuMP.Model)
# Can only process up to capacity
@constraint(model, model[:process][n, t] <= model[:capacity][n, t])
# Plant capacity can only increase over time
if t > 1
# Plant capacity can only increase over time
@constraint(model, model[:capacity][n, t] >= model[:capacity][n, t-1])
@constraint(model, model[:expansion][n, t] >= model[:expansion][n, t-1])
end
@constraint(model, model[:expansion][n, t] >= model[:expansion][n, t-1])
# Amount received equals amount processed plus stored
store_in = 0
@@ -266,14 +275,10 @@ function create_process_node_constraints!(model::JuMP.Model)
# Plant is currently open if it was already open in the previous time period or
# if it was built just now
if t > 1
@constraint(
model,
model[:is_open][n, t] == model[:is_open][n, t-1] + model[:open_plant][n, t]
)
else
@constraint(model, model[:is_open][n, t] == model[:open_plant][n, t])
end
@constraint(
model,
model[:is_open][n, t] == model[:is_open][n, t-1] + model[:open_plant][n, t]
)
# Plant can only be opened during building period
if t model[:instance].building_period

View File

@@ -96,7 +96,10 @@ function get_solution(model::JuMP.Model; marginal_costs = true)
"Expansion cost (\$)" => [
(
if t == 1
slope_open(plant, t) * JuMP.value(model[:expansion][process_node, t])
slope_open(plant, t) * (
JuMP.value(model[:expansion][process_node, t]) -
model[:expansion][process_node, 0]
)
else
slope_open(plant, t) * (
JuMP.value(model[:expansion][process_node, t]) -

View File

@@ -59,6 +59,7 @@ function _fix_plants!(model_old, model_new)::Nothing
# Fix is_open variables
for ((node_old, t), var_old) in model_old[:is_open]
t > 0 || continue
value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[(
node_old.location.plant_name,
@@ -84,6 +85,7 @@ function _fix_plants!(model_old, model_new)::Nothing
# Fix plant expansion
for ((node_old, t), var_old) in model_old[:expansion]
t > 0 || continue
value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[(
node_old.location.plant_name,

View File

@@ -65,6 +65,9 @@
"longitude (deg)": {
"type": "number"
},
"initial capacity (tonne)": {
"type": "number"
},
"disposal": {
"type": "object",
"additionalProperties": {