|
|
|
@ -27,39 +27,50 @@ end
|
|
|
|
|
function create_vars!(model::ManufacturingModel)
|
|
|
|
|
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
|
|
|
|
|
|
|
|
|
vars.flow = Dict((a, t) => @variable(mip, lower_bound=0)
|
|
|
|
|
for a in graph.arcs, t in 1:T)
|
|
|
|
|
vars.flow = Dict((a, t) => @variable(mip, lower_bound = 0) for a in graph.arcs, t = 1:T)
|
|
|
|
|
|
|
|
|
|
vars.dispose = Dict((n, t) => @variable(mip,
|
|
|
|
|
vars.dispose = Dict(
|
|
|
|
|
(n, t) => @variable(
|
|
|
|
|
mip,
|
|
|
|
|
lower_bound = 0,
|
|
|
|
|
upper_bound=n.location.disposal_limit[n.product][t])
|
|
|
|
|
for n in values(graph.plant_shipping_nodes), t in 1:T)
|
|
|
|
|
upper_bound = n.location.disposal_limit[n.product][t]
|
|
|
|
|
) for n in values(graph.plant_shipping_nodes), t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.store = Dict((n, t) => @variable(mip,
|
|
|
|
|
lower_bound=0,
|
|
|
|
|
upper_bound=n.location.storage_limit)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
vars.store = Dict(
|
|
|
|
|
(n, t) =>
|
|
|
|
|
@variable(mip, lower_bound = 0, upper_bound = n.location.storage_limit) for
|
|
|
|
|
n in values(graph.process_nodes), t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.process = Dict((n, t) => @variable(mip,
|
|
|
|
|
lower_bound = 0)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
vars.process = Dict(
|
|
|
|
|
(n, t) => @variable(mip, lower_bound = 0) for n in values(graph.process_nodes),
|
|
|
|
|
t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.open_plant = Dict((n, t) => @variable(mip, binary=true)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
vars.open_plant = Dict(
|
|
|
|
|
(n, t) => @variable(mip, binary = true) for n in values(graph.process_nodes),
|
|
|
|
|
t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.is_open = Dict((n, t) => @variable(mip, binary=true)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
vars.is_open = Dict(
|
|
|
|
|
(n, t) => @variable(mip, binary = true) for n in values(graph.process_nodes),
|
|
|
|
|
t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.capacity = Dict((n, t) => @variable(mip,
|
|
|
|
|
lower_bound = 0,
|
|
|
|
|
upper_bound = n.location.sizes[2].capacity)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
vars.capacity = Dict(
|
|
|
|
|
(n, t) =>
|
|
|
|
|
@variable(mip, lower_bound = 0, upper_bound = n.location.sizes[2].capacity)
|
|
|
|
|
for n in values(graph.process_nodes), t = 1:T
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vars.expansion = Dict((n, t) => @variable(mip,
|
|
|
|
|
vars.expansion = Dict(
|
|
|
|
|
(n, t) => @variable(
|
|
|
|
|
mip,
|
|
|
|
|
lower_bound = 0,
|
|
|
|
|
upper_bound = n.location.sizes[2].capacity -
|
|
|
|
|
n.location.sizes[1].capacity)
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T)
|
|
|
|
|
upper_bound = n.location.sizes[2].capacity - n.location.sizes[1].capacity
|
|
|
|
|
) for n in values(graph.process_nodes), t = 1:T
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -86,7 +97,7 @@ function create_objective_function!(model::ManufacturingModel)
|
|
|
|
|
obj = AffExpr(0.0)
|
|
|
|
|
|
|
|
|
|
# Process node costs
|
|
|
|
|
for n in values(graph.process_nodes), t in 1:T
|
|
|
|
|
for n in values(graph.process_nodes), t = 1:T
|
|
|
|
|
|
|
|
|
|
# Transportation and variable operating costs
|
|
|
|
|
for a in n.incoming_arcs
|
|
|
|
@ -95,49 +106,45 @@ function create_objective_function!(model::ManufacturingModel)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Opening costs
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
n.location.sizes[1].opening_cost[t],
|
|
|
|
|
vars.open_plant[n, t])
|
|
|
|
|
add_to_expression!(obj, n.location.sizes[1].opening_cost[t], vars.open_plant[n, t])
|
|
|
|
|
|
|
|
|
|
# Fixed operating costs (base)
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
add_to_expression!(
|
|
|
|
|
obj,
|
|
|
|
|
n.location.sizes[1].fixed_operating_cost[t],
|
|
|
|
|
vars.is_open[n, t])
|
|
|
|
|
vars.is_open[n, t],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Fixed operating costs (expansion)
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
slope_fix_oper_cost(n.location, t),
|
|
|
|
|
vars.expansion[n, t])
|
|
|
|
|
add_to_expression!(obj, slope_fix_oper_cost(n.location, t), vars.expansion[n, t])
|
|
|
|
|
|
|
|
|
|
# Processing costs
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
add_to_expression!(
|
|
|
|
|
obj,
|
|
|
|
|
n.location.sizes[1].variable_operating_cost[t],
|
|
|
|
|
vars.process[n, t])
|
|
|
|
|
vars.process[n, t],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Storage costs
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
n.location.storage_cost[t],
|
|
|
|
|
vars.store[n, t])
|
|
|
|
|
add_to_expression!(obj, n.location.storage_cost[t], vars.store[n, t])
|
|
|
|
|
|
|
|
|
|
# Expansion costs
|
|
|
|
|
if t < T
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
add_to_expression!(
|
|
|
|
|
obj,
|
|
|
|
|
slope_open(n.location, t) - slope_open(n.location, t + 1),
|
|
|
|
|
vars.expansion[n, t])
|
|
|
|
|
vars.expansion[n, t],
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
slope_open(n.location, t),
|
|
|
|
|
vars.expansion[n, t])
|
|
|
|
|
add_to_expression!(obj, slope_open(n.location, t), vars.expansion[n, t])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Shipping node costs
|
|
|
|
|
for n in values(graph.plant_shipping_nodes), t in 1:T
|
|
|
|
|
for n in values(graph.plant_shipping_nodes), t = 1:T
|
|
|
|
|
|
|
|
|
|
# Disposal costs
|
|
|
|
|
add_to_expression!(obj,
|
|
|
|
|
n.location.disposal_cost[n.product][t],
|
|
|
|
|
vars.dispose[n, t])
|
|
|
|
|
add_to_expression!(obj, n.location.disposal_cost[n.product][t], vars.dispose[n, t])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@objective(mip, Min, obj)
|
|
|
|
@ -150,19 +157,22 @@ function create_shipping_node_constraints!(model::ManufacturingModel)
|
|
|
|
|
|
|
|
|
|
eqs.balance = OrderedDict()
|
|
|
|
|
|
|
|
|
|
for t in 1:T
|
|
|
|
|
for t = 1:T
|
|
|
|
|
# Collection centers
|
|
|
|
|
for n in graph.collection_shipping_nodes
|
|
|
|
|
eqs.balance[n, t] = @constraint(mip,
|
|
|
|
|
sum(vars.flow[a, t] for a in n.outgoing_arcs)
|
|
|
|
|
== n.location.amount[t])
|
|
|
|
|
eqs.balance[n, t] = @constraint(
|
|
|
|
|
mip,
|
|
|
|
|
sum(vars.flow[a, t] for a in n.outgoing_arcs) == n.location.amount[t]
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Plants
|
|
|
|
|
for n in graph.plant_shipping_nodes
|
|
|
|
|
@constraint(mip,
|
|
|
|
|
@constraint(
|
|
|
|
|
mip,
|
|
|
|
|
sum(vars.flow[a, t] for a in n.incoming_arcs) ==
|
|
|
|
|
sum(vars.flow[a, t] for a in n.outgoing_arcs) + vars.dispose[n, t])
|
|
|
|
|
sum(vars.flow[a, t] for a in n.outgoing_arcs) + vars.dispose[n, t]
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
@ -172,7 +182,7 @@ end
|
|
|
|
|
function create_process_node_constraints!(model::ManufacturingModel)
|
|
|
|
|
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
|
|
|
|
|
|
|
|
|
for t in 1:T, n in graph.process_nodes
|
|
|
|
|
for t = 1:T, n in graph.process_nodes
|
|
|
|
|
input_sum = AffExpr(0.0)
|
|
|
|
|
for a in n.incoming_arcs
|
|
|
|
|
add_to_expression!(input_sum, 1.0, vars.flow[a, t])
|
|
|
|
@ -184,13 +194,22 @@ function create_process_node_constraints!(model::ManufacturingModel)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# If plant is closed, capacity is zero
|
|
|
|
|
@constraint(mip, vars.capacity[n, t] <= n.location.sizes[2].capacity * vars.is_open[n, t])
|
|
|
|
|
@constraint(
|
|
|
|
|
mip,
|
|
|
|
|
vars.capacity[n, t] <= n.location.sizes[2].capacity * vars.is_open[n, t]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# If plant is open, capacity is greater than base
|
|
|
|
|
@constraint(mip, vars.capacity[n, t] >= n.location.sizes[1].capacity * vars.is_open[n, t])
|
|
|
|
|
@constraint(
|
|
|
|
|
mip,
|
|
|
|
|
vars.capacity[n, t] >= n.location.sizes[1].capacity * vars.is_open[n, t]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Capacity is linked to expansion
|
|
|
|
|
@constraint(mip, vars.capacity[n, t] <= n.location.sizes[1].capacity + vars.expansion[n, t])
|
|
|
|
|
@constraint(
|
|
|
|
|
mip,
|
|
|
|
|
vars.capacity[n, t] <= n.location.sizes[1].capacity + vars.expansion[n, t]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Can only process up to capacity
|
|
|
|
|
@constraint(mip, vars.process[n, t] <= vars.capacity[n, t])
|
|
|
|
@ -209,14 +228,16 @@ function create_process_node_constraints!(model::ManufacturingModel)
|
|
|
|
|
if t == T
|
|
|
|
|
@constraint(mip, vars.store[n, t] == 0)
|
|
|
|
|
end
|
|
|
|
|
@constraint(mip,
|
|
|
|
|
input_sum + store_in == vars.store[n, t] + vars.process[n, t])
|
|
|
|
|
@constraint(mip, input_sum + store_in == vars.store[n, t] + vars.process[n, t])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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(mip, vars.is_open[n, t] == vars.is_open[n, t-1] + vars.open_plant[n, t])
|
|
|
|
|
@constraint(
|
|
|
|
|
mip,
|
|
|
|
|
vars.is_open[n, t] == vars.is_open[n, t-1] + vars.open_plant[n, t]
|
|
|
|
|
)
|
|
|
|
|
else
|
|
|
|
|
@constraint(mip, vars.is_open[n, t] == vars.open_plant[n, t])
|
|
|
|
|
end
|
|
|
|
@ -231,7 +252,8 @@ end
|
|
|
|
|
default_milp_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
|
|
|
default_lp_optimizer = optimizer_with_attributes(Clp.Optimizer, "LogLevel" => 0)
|
|
|
|
|
|
|
|
|
|
function solve(instance::Instance;
|
|
|
|
|
function solve(
|
|
|
|
|
instance::Instance;
|
|
|
|
|
optimizer = nothing,
|
|
|
|
|
output = nothing,
|
|
|
|
|
marginal_costs = true,
|
|
|
|
@ -248,7 +270,10 @@ function solve(instance::Instance;
|
|
|
|
|
@info @sprintf(" %12d time periods", instance.time)
|
|
|
|
|
@info @sprintf(" %12d process nodes", length(graph.process_nodes))
|
|
|
|
|
@info @sprintf(" %12d shipping nodes (plant)", length(graph.plant_shipping_nodes))
|
|
|
|
|
@info @sprintf(" %12d shipping nodes (collection)", length(graph.collection_shipping_nodes))
|
|
|
|
|
@info @sprintf(
|
|
|
|
|
" %12d shipping nodes (collection)",
|
|
|
|
|
length(graph.collection_shipping_nodes)
|
|
|
|
|
)
|
|
|
|
|
@info @sprintf(" %12d arcs", length(graph.arcs))
|
|
|
|
|
|
|
|
|
|
@info "Building optimization model..."
|
|
|
|
@ -286,19 +311,13 @@ function solve(instance::Instance;
|
|
|
|
|
return solution
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function solve(filename::AbstractString;
|
|
|
|
|
heuristic=false,
|
|
|
|
|
kwargs...,
|
|
|
|
|
)
|
|
|
|
|
function solve(filename::AbstractString; heuristic = false, kwargs...)
|
|
|
|
|
@info "Reading $filename..."
|
|
|
|
|
instance = RELOG.parsefile(filename)
|
|
|
|
|
if heuristic && instance.time > 1
|
|
|
|
|
@info "Solving single-period version..."
|
|
|
|
|
compressed = _compress(instance)
|
|
|
|
|
csol = solve(compressed;
|
|
|
|
|
output=nothing,
|
|
|
|
|
marginal_costs=false,
|
|
|
|
|
kwargs...)
|
|
|
|
|
csol = solve(compressed; output = nothing, marginal_costs = false, kwargs...)
|
|
|
|
|
@info "Filtering candidate locations..."
|
|
|
|
|
selected_pairs = []
|
|
|
|
|
for (plant_name, plant_dict) in csol["Plants"]
|
|
|
|
@ -320,10 +339,9 @@ function solve(filename::AbstractString;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function get_solution(model::ManufacturingModel;
|
|
|
|
|
marginal_costs=true,
|
|
|
|
|
)
|
|
|
|
|
mip, vars, eqs, graph, instance = model.mip, model.vars, model.eqs, model.graph, model.instance
|
|
|
|
|
function get_solution(model::ManufacturingModel; marginal_costs = true)
|
|
|
|
|
mip, vars, eqs, graph, instance =
|
|
|
|
|
model.mip, model.vars, model.eqs, model.graph, model.instance
|
|
|
|
|
T = instance.time
|
|
|
|
|
|
|
|
|
|
output = OrderedDict(
|
|
|
|
@ -339,10 +357,8 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
"Storage (\$)" => zeros(T),
|
|
|
|
|
"Total (\$)" => zeros(T),
|
|
|
|
|
),
|
|
|
|
|
"Energy" => OrderedDict(
|
|
|
|
|
"Plants (GJ)" => zeros(T),
|
|
|
|
|
"Transportation (GJ)" => zeros(T),
|
|
|
|
|
),
|
|
|
|
|
"Energy" =>
|
|
|
|
|
OrderedDict("Plants (GJ)" => zeros(T), "Transportation (GJ)" => zeros(T)),
|
|
|
|
|
"Emissions" => OrderedDict(
|
|
|
|
|
"Plants (tonne)" => OrderedDict(),
|
|
|
|
|
"Transportation (tonne)" => OrderedDict(),
|
|
|
|
@ -362,8 +378,10 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
if marginal_costs
|
|
|
|
|
for n in graph.collection_shipping_nodes
|
|
|
|
|
location_dict = OrderedDict{Any,Any}(
|
|
|
|
|
"Marginal cost (\$/tonne)" => [round(abs(JuMP.shadow_price(eqs.balance[n, t])), digits=2)
|
|
|
|
|
for t in 1:T]
|
|
|
|
|
"Marginal cost (\$/tonne)" => [
|
|
|
|
|
round(abs(JuMP.shadow_price(eqs.balance[n, t])), digits = 2) for
|
|
|
|
|
t = 1:T
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
if n.product.name ∉ keys(output["Products"])
|
|
|
|
|
output["Products"][n.product.name] = OrderedDict()
|
|
|
|
@ -378,54 +396,57 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
process_node = plant_to_process_node[plant]
|
|
|
|
|
plant_dict = OrderedDict{Any,Any}(
|
|
|
|
|
"Input" => OrderedDict(),
|
|
|
|
|
"Output" => OrderedDict(
|
|
|
|
|
"Send" => OrderedDict(),
|
|
|
|
|
"Dispose" => OrderedDict(),
|
|
|
|
|
),
|
|
|
|
|
"Output" =>
|
|
|
|
|
OrderedDict("Send" => OrderedDict(), "Dispose" => OrderedDict()),
|
|
|
|
|
"Input product" => plant.input.name,
|
|
|
|
|
"Total input (tonne)" => [0.0 for t in 1:T],
|
|
|
|
|
"Total input (tonne)" => [0.0 for t = 1:T],
|
|
|
|
|
"Total output" => OrderedDict(),
|
|
|
|
|
"Latitude (deg)" => plant.latitude,
|
|
|
|
|
"Longitude (deg)" => plant.longitude,
|
|
|
|
|
"Capacity (tonne)" => [JuMP.value(vars.capacity[process_node, t])
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Opening cost (\$)" => [JuMP.value(vars.open_plant[process_node, t]) *
|
|
|
|
|
plant.sizes[1].opening_cost[t]
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Fixed operating cost (\$)" => [JuMP.value(vars.is_open[process_node, t]) *
|
|
|
|
|
"Capacity (tonne)" =>
|
|
|
|
|
[JuMP.value(vars.capacity[process_node, t]) for t = 1:T],
|
|
|
|
|
"Opening cost (\$)" => [
|
|
|
|
|
JuMP.value(vars.open_plant[process_node, t]) *
|
|
|
|
|
plant.sizes[1].opening_cost[t] for t = 1:T
|
|
|
|
|
],
|
|
|
|
|
"Fixed operating cost (\$)" => [
|
|
|
|
|
JuMP.value(vars.is_open[process_node, t]) *
|
|
|
|
|
plant.sizes[1].fixed_operating_cost[t] +
|
|
|
|
|
JuMP.value(vars.expansion[process_node, t]) *
|
|
|
|
|
slope_fix_oper_cost(plant, t)
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Expansion cost (\$)" => [(if t == 1
|
|
|
|
|
JuMP.value(vars.expansion[process_node, t]) * slope_fix_oper_cost(plant, t) for t = 1:T
|
|
|
|
|
],
|
|
|
|
|
"Expansion cost (\$)" => [
|
|
|
|
|
(
|
|
|
|
|
if t == 1
|
|
|
|
|
slope_open(plant, t) * JuMP.value(vars.expansion[process_node, t])
|
|
|
|
|
else
|
|
|
|
|
slope_open(plant, t) * (
|
|
|
|
|
JuMP.value(vars.expansion[process_node, t]) -
|
|
|
|
|
JuMP.value(vars.expansion[process_node, t-1])
|
|
|
|
|
)
|
|
|
|
|
end)
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Process (tonne)" => [JuMP.value(vars.process[process_node, t])
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Variable operating cost (\$)" => [JuMP.value(vars.process[process_node, t]) *
|
|
|
|
|
plant.sizes[1].variable_operating_cost[t]
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Storage (tonne)" => [JuMP.value(vars.store[process_node, t])
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
"Storage cost (\$)" => [JuMP.value(vars.store[process_node, t]) *
|
|
|
|
|
plant.storage_cost[t]
|
|
|
|
|
for t in 1:T],
|
|
|
|
|
end
|
|
|
|
|
) for t = 1:T
|
|
|
|
|
],
|
|
|
|
|
"Process (tonne)" =>
|
|
|
|
|
[JuMP.value(vars.process[process_node, t]) for t = 1:T],
|
|
|
|
|
"Variable operating cost (\$)" => [
|
|
|
|
|
JuMP.value(vars.process[process_node, t]) *
|
|
|
|
|
plant.sizes[1].variable_operating_cost[t] for t = 1:T
|
|
|
|
|
],
|
|
|
|
|
"Storage (tonne)" => [JuMP.value(vars.store[process_node, t]) for t = 1:T],
|
|
|
|
|
"Storage cost (\$)" => [
|
|
|
|
|
JuMP.value(vars.store[process_node, t]) * plant.storage_cost[t] for t = 1:T
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
output["Costs"]["Fixed operating (\$)"] += plant_dict["Fixed operating cost (\$)"]
|
|
|
|
|
output["Costs"]["Variable operating (\$)"] += plant_dict["Variable operating cost (\$)"]
|
|
|
|
|
output["Costs"]["Variable operating (\$)"] +=
|
|
|
|
|
plant_dict["Variable operating cost (\$)"]
|
|
|
|
|
output["Costs"]["Opening (\$)"] += plant_dict["Opening cost (\$)"]
|
|
|
|
|
output["Costs"]["Expansion (\$)"] += plant_dict["Expansion cost (\$)"]
|
|
|
|
|
output["Costs"]["Storage (\$)"] += plant_dict["Storage cost (\$)"]
|
|
|
|
|
|
|
|
|
|
# Inputs
|
|
|
|
|
for a in process_node.incoming_arcs
|
|
|
|
|
vals = [JuMP.value(vars.flow[a, t]) for t in 1:T]
|
|
|
|
|
vals = [JuMP.value(vars.flow[a, t]) for t = 1:T]
|
|
|
|
|
if sum(vals) <= 1e-3
|
|
|
|
|
continue
|
|
|
|
|
end
|
|
|
|
@ -435,19 +456,16 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
"Distance (km)" => a.values["distance"],
|
|
|
|
|
"Latitude (deg)" => a.source.location.latitude,
|
|
|
|
|
"Longitude (deg)" => a.source.location.longitude,
|
|
|
|
|
"Transportation cost (\$)" => a.source.product.transportation_cost .*
|
|
|
|
|
vals .*
|
|
|
|
|
a.values["distance"],
|
|
|
|
|
"Transportation energy (J)" => vals .*
|
|
|
|
|
a.values["distance"] .*
|
|
|
|
|
a.source.product.transportation_energy,
|
|
|
|
|
"Transportation cost (\$)" =>
|
|
|
|
|
a.source.product.transportation_cost .* vals .* a.values["distance"],
|
|
|
|
|
"Transportation energy (J)" =>
|
|
|
|
|
vals .* a.values["distance"] .* a.source.product.transportation_energy,
|
|
|
|
|
"Emissions (tonne)" => OrderedDict(),
|
|
|
|
|
)
|
|
|
|
|
emissions_dict = output["Emissions"]["Transportation (tonne)"]
|
|
|
|
|
for (em_name, em_values) in a.source.product.transportation_emissions
|
|
|
|
|
dict["Emissions (tonne)"][em_name] = em_values .*
|
|
|
|
|
dict["Amount (tonne)"] .*
|
|
|
|
|
a.values["distance"]
|
|
|
|
|
dict["Emissions (tonne)"][em_name] =
|
|
|
|
|
em_values .* dict["Amount (tonne)"] .* a.values["distance"]
|
|
|
|
|
if em_name ∉ keys(emissions_dict)
|
|
|
|
|
emissions_dict[em_name] = zeros(T)
|
|
|
|
|
end
|
|
|
|
@ -467,7 +485,8 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
plant_dict["Input"][plant_name][location_name] = dict
|
|
|
|
|
plant_dict["Total input (tonne)"] += vals
|
|
|
|
|
output["Costs"]["Transportation (\$)"] += dict["Transportation cost (\$)"]
|
|
|
|
|
output["Energy"]["Transportation (GJ)"] += dict["Transportation energy (J)"] / 1e9
|
|
|
|
|
output["Energy"]["Transportation (GJ)"] +=
|
|
|
|
|
dict["Transportation energy (J)"] / 1e9
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
plant_dict["Energy (GJ)"] = plant_dict["Total input (tonne)"] .* plant.energy
|
|
|
|
@ -476,7 +495,8 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
plant_dict["Emissions (tonne)"] = OrderedDict()
|
|
|
|
|
emissions_dict = output["Emissions"]["Plants (tonne)"]
|
|
|
|
|
for (em_name, em_values) in plant.emissions
|
|
|
|
|
plant_dict["Emissions (tonne)"][em_name] = em_values .* plant_dict["Total input (tonne)"]
|
|
|
|
|
plant_dict["Emissions (tonne)"][em_name] =
|
|
|
|
|
em_values .* plant_dict["Total input (tonne)"]
|
|
|
|
|
if em_name ∉ keys(emissions_dict)
|
|
|
|
|
emissions_dict[em_name] = zeros(T)
|
|
|
|
|
end
|
|
|
|
@ -489,21 +509,23 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
plant_dict["Total output"][product_name] = zeros(T)
|
|
|
|
|
plant_dict["Output"]["Send"][product_name] = product_dict = OrderedDict()
|
|
|
|
|
|
|
|
|
|
disposal_amount = [JuMP.value(vars.dispose[shipping_node, t]) for t in 1:T]
|
|
|
|
|
disposal_amount = [JuMP.value(vars.dispose[shipping_node, t]) for t = 1:T]
|
|
|
|
|
if sum(disposal_amount) > 1e-5
|
|
|
|
|
skip_plant = false
|
|
|
|
|
plant_dict["Output"]["Dispose"][product_name] = disposal_dict = OrderedDict()
|
|
|
|
|
disposal_dict["Amount (tonne)"] = [JuMP.value(model.vars.dispose[shipping_node, t])
|
|
|
|
|
for t in 1:T]
|
|
|
|
|
disposal_dict["Cost (\$)"] = [disposal_dict["Amount (tonne)"][t] *
|
|
|
|
|
plant.disposal_cost[shipping_node.product][t]
|
|
|
|
|
for t in 1:T]
|
|
|
|
|
plant_dict["Output"]["Dispose"][product_name] =
|
|
|
|
|
disposal_dict = OrderedDict()
|
|
|
|
|
disposal_dict["Amount (tonne)"] =
|
|
|
|
|
[JuMP.value(model.vars.dispose[shipping_node, t]) for t = 1:T]
|
|
|
|
|
disposal_dict["Cost (\$)"] = [
|
|
|
|
|
disposal_dict["Amount (tonne)"][t] *
|
|
|
|
|
plant.disposal_cost[shipping_node.product][t] for t = 1:T
|
|
|
|
|
]
|
|
|
|
|
plant_dict["Total output"][product_name] += disposal_amount
|
|
|
|
|
output["Costs"]["Disposal (\$)"] += disposal_dict["Cost (\$)"]
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
for a in shipping_node.outgoing_arcs
|
|
|
|
|
vals = [JuMP.value(vars.flow[a, t]) for t in 1:T]
|
|
|
|
|
vals = [JuMP.value(vars.flow[a, t]) for t = 1:T]
|
|
|
|
|
if sum(vals) <= 1e-3
|
|
|
|
|
continue
|
|
|
|
|
end
|
|
|
|
@ -517,7 +539,8 @@ function get_solution(model::ManufacturingModel;
|
|
|
|
|
if a.dest.location.plant_name ∉ keys(product_dict)
|
|
|
|
|
product_dict[a.dest.location.plant_name] = OrderedDict()
|
|
|
|
|
end
|
|
|
|
|
product_dict[a.dest.location.plant_name][a.dest.location.location_name] = dict
|
|
|
|
|
product_dict[a.dest.location.plant_name][a.dest.location.location_name] =
|
|
|
|
|
dict
|
|
|
|
|
plant_dict["Total output"][product_name] += vals
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|