9 Commits

Author SHA1 Message Date
Kavitha G Menon
2cf97d6bee Capacity expansion model
Add the model file with capacity expansion constraints and subsequent modifications
2024-08-09 15:59:15 -05:00
9e0f8c5796 solve: Allow custom graph 2023-07-27 10:38:53 -05:00
5693ef2aa2 Reformat source code 2023-07-26 10:25:11 -05:00
bc05b49222 Make resolve compatible with solve(heuristic=true) 2023-07-26 10:17:37 -05:00
3e54e767c4 Fix failing test 2023-07-26 10:00:07 -05:00
84bd25b04d Fix: Remove disposal when deleting product 2023-07-07 10:22:57 -05:00
c86dda12cd Make marginal costs optional in write_reports 2023-07-07 10:20:25 -05:00
f3a2d1d616 Fix bug in _compress when plants have fixed size
Capacity was being incorrectly multiplied by T twice. This
happened because there were two references to the same struct
in the plant sizes array. This fix replaces the second reference
by an actual copy of the struct.
2023-07-07 10:05:31 -05:00
029a47a64b compress: Update disposal/acquisition limits/costs 2023-05-16 15:29:08 -05:00
10 changed files with 1766 additions and 25 deletions

1699
model-3-CapEx.jl Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -207,6 +207,8 @@ const InputPage = () => {
} }
if (outputFound) { if (outputFound) {
delete plant["outputs (tonne/tonne)"][productName]; delete plant["outputs (tonne/tonne)"][productName];
delete plant["disposal cost ($/tonne)"][productName];
delete plant["disposal limit (tonne)"][productName];
} }
} }
save(newData); save(newData);

View File

@@ -24,6 +24,9 @@ function _compress(instance::Instance)::Instance
# Compress products # Compress products
for p in compressed.products for p in compressed.products
p.acquisition_cost = [mean(p.acquisition_cost)]
p.disposal_cost = [mean(p.disposal_cost)]
p.disposal_limit = [sum(p.disposal_limit)]
p.transportation_cost = [mean(p.transportation_cost)] p.transportation_cost = [mean(p.transportation_cost)]
p.transportation_energy = [mean(p.transportation_energy)] p.transportation_energy = [mean(p.transportation_energy)]
for (emission_name, emission_value) in p.transportation_emissions for (emission_name, emission_value) in p.transportation_emissions

View File

@@ -171,7 +171,7 @@ function parse(json)::Instance
), ),
) )
end end
length(sizes) > 1 || push!(sizes, sizes[1]) length(sizes) > 1 || push!(sizes, deepcopy(sizes[1]))
sort!(sizes, by = x -> x.capacity) sort!(sizes, by = x -> x.capacity)
# Initial capacity # Initial capacity

View File

@@ -17,6 +17,24 @@ function resolve(model_old, instance::Instance; optimizer = nothing)::OrderedDic
lp_optimizer = _get_default_lp_optimizer() lp_optimizer = _get_default_lp_optimizer()
end end
@info "Filtering candidate locations..."
selected_pairs = Set()
for ((node_old, t), var_old) in model_old[:is_open]
if JuMP.value(var_old) > 0.1
push!(
selected_pairs,
(node_old.location.plant_name, node_old.location.location_name),
)
end
end
filtered_plants = []
for p in instance.plants
if (p.plant_name, p.location_name) in selected_pairs
push!(filtered_plants, p)
end
end
instance.plants = filtered_plants
@info "Building new graph..." @info "Building new graph..."
graph = build_graph(instance) graph = build_graph(instance)
_print_graph_stats(instance, graph) _print_graph_stats(instance, graph)
@@ -48,10 +66,9 @@ function _fix_plants!(model_old, model_new)::Nothing
# Fix open_plant variables # Fix open_plant variables
for ((node_old, t), var_old) in model_old[:open_plant] for ((node_old, t), var_old) in model_old[:open_plant]
value_old = JuMP.value(var_old) value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[( key = (node_old.location.plant_name, node_old.location.location_name)
node_old.location.plant_name, key keys(model_new[:graph].name_to_process_node_map) || continue
node_old.location.location_name, node_new = model_new[:graph].name_to_process_node_map[key]
)]
var_new = model_new[:open_plant][node_new, t] var_new = model_new[:open_plant][node_new, t]
JuMP.unset_binary(var_new) JuMP.unset_binary(var_new)
JuMP.fix(var_new, value_old) JuMP.fix(var_new, value_old)
@@ -61,10 +78,9 @@ function _fix_plants!(model_old, model_new)::Nothing
for ((node_old, t), var_old) in model_old[:is_open] for ((node_old, t), var_old) in model_old[:is_open]
t > 0 || continue t > 0 || continue
value_old = JuMP.value(var_old) value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[( key = (node_old.location.plant_name, node_old.location.location_name)
node_old.location.plant_name, key keys(model_new[:graph].name_to_process_node_map) || continue
node_old.location.location_name, node_new = model_new[:graph].name_to_process_node_map[key]
)]
var_new = model_new[:is_open][node_new, t] var_new = model_new[:is_open][node_new, t]
JuMP.unset_binary(var_new) JuMP.unset_binary(var_new)
JuMP.fix(var_new, value_old) JuMP.fix(var_new, value_old)
@@ -73,10 +89,9 @@ function _fix_plants!(model_old, model_new)::Nothing
# Fix plant capacities # Fix plant capacities
for ((node_old, t), var_old) in model_old[:capacity] for ((node_old, t), var_old) in model_old[:capacity]
value_old = JuMP.value(var_old) value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[( key = (node_old.location.plant_name, node_old.location.location_name)
node_old.location.plant_name, key keys(model_new[:graph].name_to_process_node_map) || continue
node_old.location.location_name, node_new = model_new[:graph].name_to_process_node_map[key]
)]
var_new = model_new[:capacity][node_new, t] var_new = model_new[:capacity][node_new, t]
JuMP.delete_lower_bound(var_new) JuMP.delete_lower_bound(var_new)
JuMP.delete_upper_bound(var_new) JuMP.delete_upper_bound(var_new)
@@ -87,10 +102,9 @@ function _fix_plants!(model_old, model_new)::Nothing
for ((node_old, t), var_old) in model_old[:expansion] for ((node_old, t), var_old) in model_old[:expansion]
t > 0 || continue t > 0 || continue
value_old = JuMP.value(var_old) value_old = JuMP.value(var_old)
node_new = model_new[:graph].name_to_process_node_map[( key = (node_old.location.plant_name, node_old.location.location_name)
node_old.location.plant_name, key keys(model_new[:graph].name_to_process_node_map) || continue
node_old.location.location_name, node_new = model_new[:graph].name_to_process_node_map[key]
)]
var_new = model_new[:expansion][node_new, t] var_new = model_new[:expansion][node_new, t]
JuMP.delete_lower_bound(var_new) JuMP.delete_lower_bound(var_new)
JuMP.delete_upper_bound(var_new) JuMP.delete_upper_bound(var_new)

View File

@@ -32,6 +32,7 @@ function solve(
output = nothing, output = nothing,
marginal_costs = true, marginal_costs = true,
return_model = false, return_model = false,
graph = nothing,
) )
if lp_optimizer == nothing if lp_optimizer == nothing
@@ -51,7 +52,9 @@ function solve(
@info "Building graph..." @info "Building graph..."
graph = RELOG.build_graph(instance) if graph === nothing
graph = RELOG.build_graph(instance)
end
_print_graph_stats(instance, graph) _print_graph_stats(instance, graph)
@info "Building optimization model..." @info "Building optimization model..."

View File

@@ -5,7 +5,7 @@
using DataFrames using DataFrames
using CSV using CSV
function products_report(solution; marginal_costs = true)::DataFrame function products_report(solution; marginal_costs)::DataFrame
df = DataFrame() df = DataFrame()
df."product name" = String[] df."product name" = String[]
df."location name" = String[] df."location name" = String[]
@@ -21,7 +21,11 @@ function products_report(solution; marginal_costs = true)::DataFrame
for (prod_name, prod_dict) in solution["Products"] for (prod_name, prod_dict) in solution["Products"]
for (location_name, location_dict) in prod_dict for (location_name, location_dict) in prod_dict
for year = 1:T for year = 1:T
marginal_cost = location_dict["Marginal cost (\$/tonne)"][year] if marginal_costs
marginal_cost = location_dict["Marginal cost (\$/tonne)"][year]
else
marginal_cost = 0.0
end
latitude = round(location_dict["Latitude (deg)"], digits = 6) latitude = round(location_dict["Latitude (deg)"], digits = 6)
longitude = round(location_dict["Longitude (deg)"], digits = 6) longitude = round(location_dict["Longitude (deg)"], digits = 6)
amount = location_dict["Amount (tonne)"][year] amount = location_dict["Amount (tonne)"][year]
@@ -49,4 +53,5 @@ function products_report(solution; marginal_costs = true)::DataFrame
return df return df
end end
write_products_report(solution, filename) = CSV.write(filename, products_report(solution)) write_products_report(solution, filename; marginal_costs = true) =
CSV.write(filename, products_report(solution; marginal_costs))

View File

@@ -13,8 +13,12 @@ function write(solution::AbstractDict, filename::AbstractString)
end end
end end
function write_reports(solution::AbstractDict, basename::AbstractString) function write_reports(
RELOG.write_products_report(solution, "$(basename)_products.csv") solution::AbstractDict,
basename::AbstractString;
marginal_costs = true,
)
RELOG.write_products_report(solution, "$(basename)_products.csv"; marginal_costs)
RELOG.write_plants_report(solution, "$(basename)_plants.csv") RELOG.write_plants_report(solution, "$(basename)_plants.csv")
RELOG.write_plant_outputs_report(solution, "$(basename)_plant_outputs.csv") RELOG.write_plant_outputs_report(solution, "$(basename)_plant_outputs.csv")
RELOG.write_plant_emissions_report(solution, "$(basename)_plant_emissions.csv") RELOG.write_plant_emissions_report(solution, "$(basename)_plant_emissions.csv")

View File

@@ -72,7 +72,10 @@ function instance_parse_test()
@test plant.sizes[1].opening_cost == [3000, 3000] @test plant.sizes[1].opening_cost == [3000, 3000]
@test plant.sizes[1].fixed_operating_cost == [50, 50] @test plant.sizes[1].fixed_operating_cost == [50, 50]
@test plant.sizes[1].variable_operating_cost == [50, 50] @test plant.sizes[1].variable_operating_cost == [50, 50]
@test plant.sizes[1] == plant.sizes[2] @test plant.sizes[2].capacity == 1000.0
@test plant.sizes[2].opening_cost == [3000, 3000]
@test plant.sizes[2].fixed_operating_cost == [50, 50]
@test plant.sizes[2].variable_operating_cost == [50, 50]
p4 = product_name_to_product["P4"] p4 = product_name_to_product["P4"]
@test plant.output[p3] == 0.05 @test plant.output[p3] == 0.05

View File

@@ -4,10 +4,18 @@
using RELOG using RELOG
function model_resolve_test() function model_resolve_test()
@testset "Resolve" begin @testset "Resolve (exact)" begin
# Shoud not crash # Shoud not crash
filename = fixture("s1.json") filename = fixture("s1.json")
solution_old, model_old = RELOG.solve(filename, return_model = true) solution_old, model_old = RELOG.solve(filename, return_model = true)
solution_new = RELOG.resolve(model_old, filename) solution_new = RELOG.resolve(model_old, filename)
end end
@testset "Resolve (heuristic)" begin
# Shoud not crash
filename = fixture("s1.json")
solution_old, model_old =
RELOG.solve(filename, return_model = true, heuristic = true)
solution_new = RELOG.resolve(model_old, filename)
end
end end