You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RELOG/model-3.jl

1200 lines
34 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# RELOG: Reverse Logistics Optimization
# Copyright (C) 2020-2024, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
using CSV
using DataFrames
using JSON
using JuMP
using OrderedCollections
using Random
include("jumpext.jl")
include("dist.jl")
dict = OrderedDict
Time = Int
Random.seed!(42)
# Structs
# =========================================================================
Base.@kwdef struct Component
name::String
end
Base.@kwdef struct Product
name::String
comp::Vector{Component}
end
Base.@kwdef struct Center
name::String
latitude::Float64
longitude::Float64
prod_out::Vector{Product}
end
Base.@kwdef struct Plant
name::String
latitude::Float64
longitude::Float64
prod_out::Vector{Product}
prod_in::Product
end
Base.@kwdef struct Emission
name::String
end
Base.show(
io::IO,
p::Union{Component,Product,Center,Plant,Emission},
) = print(io, p.name)
Base.@kwdef struct Instance
T::UnitRange{Int}
centers::Vector{Center}
plants::Vector{Plant}
products::Vector{Product}
emissions::Vector{Emission}
alpha_mix::dict{Tuple{Plant,Product,Component,Component},Float64}
alpha_plant_emission::dict{Tuple{Plant,Emission,Time},Float64}
alpha_tr_emission::dict{Tuple{Product,Emission,Time},Float64}
c_acq::dict{Tuple{Center,Product,Time},Float64}
c_center_disp::dict{Tuple{Center,Product,Time},Float64}
c_emission::dict{Tuple{Emission,Time},Float64}
c_fix::dict{Tuple{Plant,Time},Float64}
c_open::dict{Tuple{Plant,Time},Float64}
c_plant_disp::dict{Tuple{Plant,Product,Time},Float64}
c_store::dict{Tuple{Center,Product,Time},Float64}
c_tr::dict{Tuple{Product,Time},Float64}
c_var::dict{Tuple{Plant,Time},Float64}
m_cap::dict{Plant,Float64}
m_center_disp::dict{Tuple{Product,Time},Float64}
m_dist::dict{Tuple{Union{Center,Plant},Plant},Float64}
m_emission::dict{Tuple{Emission,Time},Float64}
m_init::dict{Tuple{Center,Product,Component,Time},Float64}
m_plant_disp::dict{Tuple{Plant,Product,Time},Float64}
m_store::dict{Tuple{Center,Product,Time},Float64}
end
# Generate
# ==============================================================================
function generate_data()
# Time window
T = 1:2
# Cities
cities_a = dict(
"Chicago" => [41.881832, -87.623177],
"New York City" => [40.712776, -74.005974],
"Los Angeles" => [34.052235, -118.243683],
"Houston" => [29.760427, -95.369804],
"Phoenix" => [33.448376, -112.074036],
"Philadelphia" => [39.952583, -75.165222],
"San Antonio" => [29.424122, -98.493629],
"San Diego" => [32.715736, -117.161087],
"Dallas" => [32.776664, -96.796988],
"San Jose" => [37.338208, -121.886329],
)
cities_b = dict(
"Chicago" => [41.881832, -87.623177],
"Phoenix" => [33.448376, -112.074036],
"Dallas" => [32.776664, -96.796988],
)
# Components
film = Component("Film")
paper = Component("Paper")
cardboard = Component("Cardboard")
# Products
waste = Product(
name="Waste",
comp=[film, paper, cardboard],
)
film_bale = Product(
name="Film bale",
comp=[film, paper, cardboard],
)
cardboard_bale = Product(
name="Cardboard bale",
comp=[paper, cardboard],
)
cardboard_sheets = Product(
name="Cardboard sheets",
comp=[cardboard],
)
products = [waste, film_bale, cardboard_bale, cardboard_sheets]
# Centers
centers = [
Center(
name="Collection ($city_name)",
latitude=city_lat,
longitude=city_lon,
prod_out=[waste],
)
for (city_name, (city_lat, city_lon)) in cities_a
]
# Plants
plants_a = [
Plant(
name="MRF ($city_name)",
latitude=city_lat,
longitude=city_lon,
prod_in=waste,
prod_out=[film_bale, cardboard_bale],
)
for (city_name, (city_lat, city_lon)) in cities_b
]
plants_b = [
Plant(
name="Paper Mill ($city_name)",
latitude=city_lat,
longitude=city_lon,
prod_in=cardboard_bale,
prod_out=[cardboard_sheets],
)
for (city_name, (city_lat, city_lon)) in cities_b
]
plants = [plants_a; plants_b]
# Emissions
emissions = [
Emission("CO2"),
]
alpha_mix = dict(
(p, r, cin, cout) => 0.0
for p in plants
for cin in p.prod_in.comp
for r in p.prod_out
for cout in r.comp
)
for p in plants_a
alpha_mix[p, film_bale, film, film] = 0.98
alpha_mix[p, film_bale, paper, paper] = 0.02
alpha_mix[p, film_bale, cardboard, cardboard] = 0.02
alpha_mix[p, cardboard_bale, paper, paper] = 0.02
alpha_mix[p, cardboard_bale, cardboard, cardboard] = 0.75
end
for p in plants_b
alpha_mix[p, cardboard_sheets, cardboard, cardboard] = 0.95
end
alpha_plant_emission = dict(
(p, s, t) => 0.01
for p in plants, s in emissions, t in T
)
alpha_tr_emission = dict(
(r, s, t) => 0.01
for r in products, s in emissions, t in T
)
c_acq = dict(
(q, r, t) => 1.0
for q in centers
for r in q.prod_out, t in T
)
c_center_disp = dict(
(q, r, t) => 0
for q in centers
for r in q.prod_out
for t in T
)
c_emission = dict(
(s, t) => 0.01
for s in emissions, t in T
)
c_fix = dict(
(p, t) => 1_000.0
for p in plants, t in T
)
c_open = dict(
(p, t) => 10_000.0
for p in plants, t in T
)
c_plant_disp = dict(
(p, r, t) => (r == cardboard_sheets ? -100.0 : -10.0)
for p in plants
for r in p.prod_out, t in T
)
c_store = dict(
(q, r, t) => 1.0
for q in centers
for r in q.prod_out, t in T
)
c_tr = dict(
(r, t) => 0.05
for r in products, t in T
)
c_var = dict(
(p, t) => 1.0
for p in plants, t in T
)
m_cap = dict(
p => 50000.0
for p in plants
)
m_center_disp = dict(
(r, t) => 0.0
for r in products, t in T
)
metric = KnnDrivingDistance()
m_dist = dict(
(p, q) => _calculate_distance(
p.latitude,
p.longitude,
q.latitude,
q.longitude,
metric,
)
for p in [plants; centers], q in plants
)
m_emission = dict(
(s, t) => 1_000_000
for s in emissions, t in T
)
m_init = dict()
for q in centers, r in q.prod_out
ratio = dict(
c => rand(1:10)
for c in r.comp
)
total = dict(
t => rand(1:1000)
for t in T
)
for c in r.comp, t in T
m_init[q, r, c, t] = ratio[c] * total[t]
end
end
m_plant_disp = dict(
(p, r, t) => 1_000_000
for p in plants
for r in p.prod_out
for t in T
)
m_store = dict(
(q, r, t) => 1_000
for q in centers
for r in q.prod_out, t in T
)
return Instance(;
T,
centers,
plants,
products,
emissions,
alpha_mix,
alpha_plant_emission,
alpha_tr_emission,
c_acq,
c_center_disp,
c_emission,
c_fix,
c_open,
c_plant_disp,
c_store,
c_tr,
c_var,
m_cap,
m_center_disp,
m_dist,
m_emission,
m_init,
m_plant_disp,
m_store
)
end
# Write
# ==============================================================================
function write_json(data, filename)
json = dict()
json["parameters"] = dict(
"time horizon (years)" => data.T.stop,
)
json["products"] = dict(
r.name => dict(
"components" => [c.name for c in r.comp],
"disposal limit (tonne)" => [
data.m_center_disp[r, t]
for t in data.T
],
"transportation cost (\$/km/tonne)" => [
data.c_tr[r, t]
for t in data.T
],
"transportation emissions (tonne/km/tonne)" => dict(
s => [
data.alpha_tr_emission[r, s, t]
for t in data.T
]
for s in data.emissions
)
)
for r in data.products
)
json["centers"] = dict(
q.name => dict(
"latitude" => q.latitude,
"longitude" => q.longitude,
"output" => dict(
r.name => dict(
"initial amount (tonne)" => [
data.m_init[q, r, c, t]
for c in r.comp, t in data.T
],
"disposal cost (\$/tonne)" => [
data.c_center_disp[q, r, t]
for t in data.T
],
"storage cost (\$/tonne)" => [
data.c_store[q, r, t]
for t in data.T
],
"storage limit (tonne)" => [
data.m_store[q, r, t]
for t in data.T
],
"acquisition cost (\$/tonne)" => [
data.c_acq[q, r, t]
for t in data.T
],
)
for r in q.prod_out
)
)
for q in data.centers
)
json["plants"] = dict(
p.name => dict(
"latitude" => p.latitude,
"longitude" => p.longitude,
"input" => p.prod_in.name,
"output" => dict(
r.name => dict(
"output matrix" => [
data.alpha_mix[p, r, c_in, c_out]
for c_in in p.prod_in.comp, c_out in r.comp
],
"disposal limit (tonne)" => [
data.m_plant_disp[p, r, t]
for t in data.T
],
"disposal cost (\$/tonne)" => [
data.c_plant_disp[p, r, t]
for t in data.T
],
)
for r in p.prod_out
),
"fixed operating cost (\$)" => [
data.c_fix[p, t]
for t in data.T
],
"variable operating cost (\$/tonne)" => [
data.c_var[p, t]
for t in data.T
],
"opening cost (\$)" => [
data.c_open[p, t]
for t in data.T
],
"capacity (tonne)" => data.m_cap[p],
"emissions (tonne/tonne)" => dict(
s => [
data.alpha_plant_emission[p, s, t]
for t in data.T
]
for s in data.emissions
)
)
for p in data.plants
)
json["emissions"] = dict(
s.name => dict(
"penalty (\$/tonne)" => [
data.c_emission[s, t]
for t in data.T
],
"limit (tonne)" => [
data.m_emission[s, t]
for t in data.T
]
)
for s in data.emissions
)
open(filename, "w") do io
JSON.print(io, json, 2)
end
end
# Read
# ==============================================================================
function read_json(filename)
json = JSON.parsefile(filename)
T = 1:json["parameters"]["time horizon (years)"]
centers = []
components_by_name = dict()
emissions = []
emissions_by_name = dict()
plants = []
products = []
products_by_name = dict()
alpha_mix = dict()
alpha_plant_emission = dict()
alpha_tr_emission = dict()
c_acq = dict()
c_center_disp = dict()
c_emission = dict()
c_fix = dict()
c_open = dict()
c_plant_disp = dict()
c_store = dict()
c_tr = dict()
c_var = dict()
m_cap = dict()
m_center_disp = dict()
m_emission = dict()
m_init = dict()
m_plant_disp = dict()
m_store = dict()
for (emission_name, emission_data) in json["emissions"]
s = Emission(emission_name)
emissions_by_name[emission_name] = s
push!(emissions, s)
for t in T
c_emission[s, t] = emission_data["penalty (\$/tonne)"][t]
m_emission[s, t] = emission_data["limit (tonne)"][t]
end
end
for (name, prod_data) in json["products"]
comp = []
for (comp_name) in prod_data["components"]
if comp_name keys(components_by_name)
components_by_name[comp_name] = Component(comp_name)
end
push!(comp, components_by_name[comp_name])
end
r = Product(name, comp)
products_by_name[name] = r
push!(products, r)
for t in T
m_center_disp[r, t] = prod_data["disposal limit (tonne)"][t]
c_tr[r, t] = prod_data["transportation cost (\$/km/tonne)"][t]
end
for (s_name, s_data) in prod_data["transportation emissions (tonne/km/tonne)"]
s = emissions_by_name[s_name]
for t in T
alpha_tr_emission[r, s, t] = s_data[t]
end
end
end
for (name, center_data) in json["centers"]
latitude = center_data["latitude"]
longitude = center_data["longitude"]
prod_out = [products_by_name[r] for r in keys(center_data["output"])]
q = Center(; name, latitude, longitude, prod_out)
push!(centers, q)
for r in prod_out, t in T
c_acq[q, r, t] = center_data["output"][r.name]["acquisition cost (\$/tonne)"][t]
c_center_disp[q, r, t] = center_data["output"][r.name]["disposal cost (\$/tonne)"][t]
c_store[q, r, t] = center_data["output"][r.name]["storage cost (\$/tonne)"][t]
m_store[q, r, t] = center_data["output"][r.name]["storage limit (tonne)"][t]
for (c_idx, c) in enumerate(r.comp)
m_init[q, r, c, t] = center_data["output"][r.name]["initial amount (tonne)"][t][c_idx]
end
end
end
for (plant_name, plant_data) in json["plants"]
latitude = plant_data["latitude"]
longitude = plant_data["longitude"]
prod_in = products_by_name[plant_data["input"]]
prod_out = [products_by_name[r] for r in keys(plant_data["output"])]
p = Plant(plant_name, latitude, longitude, prod_out, prod_in)
push!(plants, p)
m_cap[p] = plant_data["capacity (tonne)"]
for t in T
c_fix[p, t] = plant_data["fixed operating cost (\$)"][t]
c_var[p, t] = plant_data["variable operating cost (\$/tonne)"][t]
c_open[p, t] = plant_data["opening cost (\$)"][t]
end
for r in prod_out,
(cin_idx, c_in) in enumerate(prod_in.comp),
(cout_idx, c_out) in enumerate(r.comp)
alpha_mix[p, r, c_in, c_out] =
plant_data["output"][r.name]["output matrix"][cout_idx][cin_idx]
end
for r in prod_out, t in T
c_plant_disp[p, r, t] = plant_data["output"][r.name]["disposal cost (\$/tonne)"][t]
m_plant_disp[p, r, t] = plant_data["output"][r.name]["disposal limit (tonne)"][t]
end
for (s_name, s_data) in plant_data["emissions (tonne/tonne)"]
s = emissions_by_name[s_name]
for t in T
alpha_plant_emission[p, s, t] = s_data[t]
end
end
end
metric = KnnDrivingDistance()
m_dist = dict(
(p, q) => _calculate_distance(
p.latitude,
p.longitude,
q.latitude,
q.longitude,
metric,
)
for p in [plants; centers], q in plants
)
return Instance(;
T,
centers,
plants,
products,
emissions,
alpha_mix,
alpha_plant_emission,
alpha_tr_emission,
c_acq,
c_center_disp,
c_emission,
c_fix,
c_open,
c_plant_disp,
c_store,
c_tr,
c_var,
m_cap,
m_center_disp,
m_dist,
m_emission,
m_init,
m_plant_disp,
m_store
)
end
# Run
# ==============================================================================
function generate_json()
@info "Generating data"
data = generate_data()
@info "Writing JSON file"
write_json(data, "output-3/case.json")
end
function solve(filename, optimizer)
@info "Reading JSON file"
data = read_json(filename)
T = data.T
centers = data.centers
plants = data.plants
products = data.products
emissions = data.emissions
model = Model(optimizer)
# Graph
# -------------------------------------------------------------------------
E = []
E_in = dict(src => [] for src in plants)
E_out = dict(src => [] for src in plants centers)
function push_edge!(src, dst, r)
push!(E, (src, dst, r))
push!(E_out[src], (dst, r))
push!(E_in[dst], (src, r))
end
for r in products
# Plant to plant
for p1 in plants
r p1.prod_out || continue
for p2 in plants
p1 != p2 || continue
r == p2.prod_in || continue
push_edge!(p1, p2, r)
end
end
# Center to plant
for q in centers
r q.prod_out || continue
for p in plants
r == p.prod_in || continue
push_edge!(q, p, r)
end
end
end
# Decision variables
# -------------------------------------------------------------------------
y = _init(model, :y)
for (q, p, r) in E, c in r.comp, t in T
y[q, p, r, c, t] = @variable(model, lower_bound = 0)
end
y_total = _init(model, :y_total)
for (q, p, r) in E, t in T
y_total[q, p, r, t] = @variable(model, lower_bound = 0)
end
z_center_disp = _init(model, :z_center_disp)
for q in centers, r in q.prod_out, c in r.comp, t in T
z_center_disp[q, r, c, t] = @variable(model, lower_bound = 0)
end
z_center_disp_total = _init(model, :z_center_disp_total)
for q in centers, r in q.prod_out, t in T
z_center_disp_total[q, r, t] = @variable(model, lower_bound = 0)
end
z_store = _init(model, :z_store)
for q in centers, r in q.prod_out, c in r.comp, t in T
if t == T.stop
z_store[q, r, c, t] = 0.0
else
z_store[q, r, c, t] = @variable(model, lower_bound = 0)
end
end
z_store_total = _init(model, :z_store_total)
for q in centers, r in q.prod_out
z_store_total[q, r, 0] = 0.0
for t in T
if t == T.stop
z_store_total[q, r, t] = 0.0
else
z_store_total[q, r, t] = @variable(model, lower_bound = 0)
end
end
end
x_open = _init(model, :x_open)
for p in plants
x_open[p, 0] = 0
for t in T
x_open[p, t] = @variable(model, binary = true)
end
end
x_send = _init(model, :x_send)
for p in plants, (q, r) in E_out[p], t in T
x_send[p, q, r, t] = @variable(model, binary = true)
end
x_disp = _init(model, :x_disp)
for p in plants, r in p.prod_out, t in T
x_disp[p, r, t] = @variable(model, binary = true)
end
z_prod = _init(model, :z_prod)
for p in plants, r in p.prod_out, c in r.comp, t in T
z_prod[p, r, c, t] = @variable(model, lower_bound = 0)
end
z_plant_disp = _init(model, :z_plant_disp)
for p in plants, r in p.prod_out, c in r.comp, t in T
z_plant_disp[p, r, c, t] = @variable(model, lower_bound = 0)
end
z_tr_emissions = _init(model, :z_tr_emissions)
for (q, p, r) in E, s in emissions, t in T
z_tr_emissions[q, p, r, s, t] = @variable(model, lower_bound = 0)
end
z_plant_emissions = _init(model, :z_plant_emissions)
for p in plants, s in emissions, t in T
z_plant_emissions[p, s, t] = @variable(model, lower_bound = 0)
end
# Objective function
# -------------------------------------------------------------------------
obj = AffExpr()
# Center disposal
for q in centers, r in q.prod_out, t in T
add_to_expression!(
obj,
data.c_center_disp[q, r, t],
z_center_disp_total[q, r, t]
)
end
# Center acquisition
for q in centers, r in q.prod_out, c in r.comp, t in T
add_to_expression!(
obj,
data.m_init[q, r, c, t] * data.c_acq[q, r, t]
)
end
# Center storage
for q in centers, r in q.prod_out, t in T
add_to_expression!(
obj,
data.c_store[q, r, t],
z_store_total[q, r, t],
)
end
# Transportation
for (q, p, r) in E, c in r.comp, t in T
add_to_expression!(
obj,
data.m_dist[q, p] * data.c_tr[r, t],
y[q, p, r, c, t]
)
end
# Transportation emissions
for (q, p, r) in E, s in emissions, t in T
add_to_expression!(
obj,
data.c_emission[s, t],
z_tr_emissions[q, p, r, s, t]
)
end
# Fixed cost
for p in plants, t in T
add_to_expression!(
obj,
data.c_fix[p, t],
x_open[p, t]
)
end
# Opening cost
for p in plants, t in T
add_to_expression!(
obj,
data.c_open[p, t],
x_open[p, t] - x_open[p, t-1]
)
end
# Variable operating cost
for (q, p, r) in E, c in r.comp, t in T
add_to_expression!(
obj,
data.c_var[p, t],
y[q, p, r, c, t]
)
end
# Plant disposal
for p in plants, r in p.prod_out, c in r.comp, t in T
add_to_expression!(
obj,
data.c_plant_disp[p, r, t],
z_plant_disp[p, r, c, t]
)
end
# Plant emissions
for p in plants, s in emissions, t in T
add_to_expression!(
obj,
data.c_emission[s, t],
z_plant_emissions[p, s, t]
)
end
@objective(model, Min, obj)
# Constraints
# -------------------------------------------------------------------------
eq_balance = _init(model, :eq_balance)
for q in centers, r in q.prod_out, t in T
eq_balance[q, r, t] = @constraint(
model,
sum(
y_total[q, p, r2, t]
for (p, r2) in E_out[q] if r == r2
) + z_store_total[q, r, t] ==
sum(data.m_init[q, r, c, t] for c in r.comp) +
z_store_total[q, r, t-1]
)
end
ratio(q, r, c, t) = (
data.m_init[q, r, c, t] /
sum(data.m_init[q, r, d, t] for d in r.comp)
)
eq_y_total = _init(model, :eq_y_total)
for (q, p, r) in E, t in T
eq_y_total[q, p, r, t] = @constraint(
model,
y_total[q, p, r, t] == sum(y[q, p, r, c, t] for c in r.comp)
)
end
eq_split_y = _init(model, :eq_split_y)
for (q, p, r) in E, c in r.comp, t in T
if q centers
continue
end
eq_split_y[q, p, r, c, t] = @constraint(
model,
y[q, p, r, c, t] == ratio(q, r, c, t) * y_total[q, p, r, t]
)
end
eq_split_center_disp = _init(model, :eq_split_center_disp)
for q in centers, r in q.prod_out, c in r.comp, t in T
eq_split_center_disp[q, r, c, t] = @constraint(
model,
z_center_disp[q, r, c, t] == ratio(q, r, c, t) * z_center_disp_total[q, r, t]
)
end
eq_split_store = _init(model, :eq_split_store)
for q in centers, r in q.prod_out, c in r.comp, t in T
eq_split_store[q, r, c, t] = @constraint(
model,
z_store[q, r, c, t] == ratio(q, r, c, t) * z_store_total[q, r, t]
)
end
eq_center_disposal = _init(model, :eq_center_disposal)
for r in products, t in T
centers_r = [q for q in centers if r q.prod_out]
if isempty(centers_r)
continue
end
eq_center_disposal[r, t] = @constraint(
model,
sum(z_center_disp_total[q, r, t] for q in centers_r) <= data.m_center_disp[r, t]
)
end
eq_center_storage = _init(model, :eq_center_storage)
for q in centers, r in q.prod_out, t in T
eq_center_storage[q, r, t] = @constraint(
model,
z_store_total[q, r, t] <= data.m_store[q, r, t]
)
end
eq_tr_emissions = _init(model, :eq_tr_emissions)
for (q, p, r) in E, s in emissions, t in T
eq_tr_emissions[q, p, r, s, t] = @constraint(
model,
z_tr_emissions[q, p, r, s, t] ==
data.m_dist[q, p] * data.alpha_tr_emission[r, s, t] * y_total[q, p, r, t]
)
end
eq_plant_capacity = _init(model, :eq_plant_capacity)
for p in plants, t in T
eq_plant_capacity[p, t] = @constraint(
model,
sum(y_total[q, p, r, t] for (q, r) in E_in[p]) <=
data.m_cap[p] * x_open[p, t]
)
end
eq_plant_prod = _init(model, :eq_plant_prod)
for p in plants, r_out in p.prod_out, c_out in r_out.comp, t in T
eq_plant_prod[p, r_out, c_out, t] = @constraint(
model,
z_prod[p, r_out, c_out, t] ==
sum(
data.alpha_mix[p, r_out, c_in, c_out] * y[q, p, r_in, c_in, t]
for (q, r_in) in E_in[p]
for c_in in r_in.comp
)
)
end
eq_plant_disp = _init(model, :eq_plant_disp)
for p in plants, r in p.prod_out, t in T
eq_plant_disp[p, r, t] = @constraint(
model,
sum(
z_plant_disp[p, r, c, t]
for c in r.comp
) <= data.m_plant_disp[p, r, t] * x_disp[p, r, t]
)
end
eq_plant_emissions = _init(model, :eq_plant_emissions)
for p in plants, s in emissions, t in T
eq_plant_emissions[p, s, t] = @constraint(
model,
z_plant_emissions[p, s, t] ==
sum(
y_total[q, p, r, t]
for (q, r,) in E_in[p]
) * data.alpha_plant_emission[p, s, t]
)
end
eq_emissions_limit = _init(model, :eq_emissions_limit)
for s in emissions, t in T
eq_emissions_limit[s, t] = @constraint(
model,
sum(z_plant_emissions[p, s, t] for p in plants)
+
sum(
z_tr_emissions[q, p, r, s, t]
for (q, p, r) in E
) <= data.m_emission[s, t]
)
end
eq_plant_remains_open = _init(model, :eq_plant_remains_open)
for p in plants, t in T
eq_plant_remains_open[p, t] = @constraint(
model,
x_open[p, t] >= x_open[p, t-1]
)
end
eq_plant_single_dest = _init(model, :eq_plant_single_dest)
for p in plants, r in p.prod_out, t in T
eq_plant_single_dest[p, r, t] = @constraint(
model,
sum(
x_send[p, q, r, t]
for (q, r2) in E_out[p]
if r == r2
) + x_disp[p, r, t] <= 1
)
end
eq_plant_send_limit = _init(model, :eq_plant_send_limit)
for p in plants, (q, r) in E_out[p], t in T
eq_plant_send_limit[p, q, r, t] = @constraint(
model,
y_total[p, q, r, t] <= data.m_cap[q] * x_send[p, q, r, t]
)
end
eq_plant_balance = _init(model, :eq_plant_balance)
for p in plants, r in p.prod_out, c in r.comp, t in T
eq_plant_balance[p, r, c, t] = @constraint(
model,
z_prod[p, r, c, t] ==
z_plant_disp[p, r, c, t] +
sum(
y[p, q, r, c, t]
for (q, r2) in E_out[p]
if r == r2
)
)
end
# Optimize
# -------------------------------------------------------------------------
_set_names!(model)
optimize!(model)
# Report: Transportation
# -------------------------------------------------------------------------
output_dir = dirname(filename)
df = DataFrame()
df."source" = String[]
df."destination" = String[]
df."product" = String[]
df."component" = String[]
df."time" = Int[]
df."distance (km)" = Float64[]
df."amount sent (tonne)" = Float64[]
df."transportation cost (\$)" = Float64[]
df."variable operating cost (\$)" = Float64[]
for (q, p, r) in E, c in r.comp, t in T
if value(y[q, p, r, c, t]) 0
continue
end
push!(
df,
[
q.name,
p.name,
r.name,
c.name,
t,
data.m_dist[q, p],
value(y[q, p, r, c, t]),
data.m_dist[q, p] * data.c_tr[r, t] * value(y[q, p, r, c, t]),
data.c_var[p, t] * value(y[q, p, r, c, t])
]
)
end
CSV.write("$output_dir/transp.csv", df)
# Report: Centers
# -------------------------------------------------------------------------
df = DataFrame()
df."center" = String[]
df."product" = String[]
df."component" = String[]
df."time" = Int[]
df."amount available (tonne)" = Float64[]
df."amount sent (tonne)" = Float64[]
df."amount stored (tonne)" = Float64[]
df."amount disposed (tonne)" = Float64[]
df."acquisition cost (\$)" = Float64[]
df."storage cost (\$)" = Float64[]
df."disposal cost (\$)" = Float64[]
for q in centers, r in q.prod_out, c in r.comp, t in T
push!(
df,
[
q.name,
r.name,
c.name,
t,
data.m_init[q, r, c, t],
sum(
value(y[q, p, r, c, t])
for (p, r2) in E_out[q]
if r == r2
),
value(z_store[q, r, c, t]),
value(z_center_disp[q, r, c, t]),
data.m_init[q, r, c, t] * data.c_acq[q, r, t],
data.c_store[q, r, t] * value(z_store[q, r, c, t]),
data.c_center_disp[q, r, t] * value(z_center_disp[q, r, c, t]),
]
)
end
CSV.write("$output_dir/centers.csv", df)
# Report: Plants
# -------------------------------------------------------------------------
df = DataFrame()
df."plant" = String[]
df."time" = Int[]
df."is open?" = Float64[]
df."opening cost (\$)" = Float64[]
df."fixed operating cost (\$)" = Float64[]
for p in plants, t in T
push!(
df,
[
p.name,
t,
value(x_open[p, t]),
data.c_open[p, t] * (value(x_open[p, t]) - value(x_open[p, t-1])),
data.c_fix[p, t] * value(x_open[p, t]),
]
)
end
CSV.write("$output_dir/plants.csv", df)
# Report: Plant Outputs
# -------------------------------------------------------------------------
df = DataFrame()
df."plant" = String[]
df."product" = String[]
df."component" = String[]
df."time" = Int[]
df."amount produced (tonne)" = Float64[]
df."amount disposed (tonne)" = Float64[]
df."amount sent (tonne)" = Float64[]
df."disposal cost (\$)" = Float64[]
for p in plants, r in p.prod_out, c in r.comp, t in T
if value(z_prod[p, r, c, t]) 0
continue
end
push!(
df,
[
p.name,
r.name,
c.name,
t,
value(z_prod[p, r, c, t]),
value(z_plant_disp[p, r, c, t]),
sum(
value(y[p, q, r, c, t])
for (q, r2) in E_out[p]
if r == r2;
init=0.0
),
data.c_plant_disp[p, r, t] * value(z_plant_disp[p, r, c, t]),
]
)
end
CSV.write("$output_dir/plant-outputs.csv", df)
# Report: Plant Emissions
# -------------------------------------------------------------------------
df = DataFrame()
df."plant" = String[]
df."emission" = String[]
df."time" = Int[]
df."amount emitted (tonne)" = Float64[]
df."emission cost (\$)" = Float64[]
for p in plants, s in emissions, t in T
push!(
df,
[
p.name,
s.name,
t,
value(z_plant_emissions[p, s, t]),
data.c_emission[s, t] * value(z_plant_emissions[p, s, t]),
]
)
end
CSV.write("$output_dir/plant-emissions.csv", df)
# Report: Transportation Emissions
# -------------------------------------------------------------------------
df = DataFrame()
df."source" = String[]
df."destination" = String[]
df."product" = String[]
df."emission" = String[]
df."time" = Int[]
df."distance (km)" = Float64[]
df."amount sent (tonne)" = Float64[]
df."amount emitted (tonne)" = Float64[]
df."emission cost (\$)" = Float64[]
for (q, p, r) in E, s in emissions, t in T
if value(y_total[q, p, r, t]) 0
continue
end
push!(
df,
[
q.name,
p.name,
r.name,
s.name,
t,
data.m_dist[q, p],
value(y_total[q, p, r, t]),
value(z_tr_emissions[q, p, r, s, t]),
data.c_emission[s, t] * value(z_tr_emissions[q, p, r, s, t]),
]
)
end
CSV.write("$output_dir/transp-emissions.csv", df)
return
end