Allow disposal at collection centers

feature/stochastic
Alinson S. Xavier 3 years ago
parent 57b7d09c08
commit e915a57e58

@ -11,6 +11,10 @@ All notable changes to this project will be documented in this file.
[semver]: https://semver.org/spec/v2.0.0.html [semver]: https://semver.org/spec/v2.0.0.html
[pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0 [pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
## [Unreleased]
- Allow product disposal at collection centers
## [0.5.1] -- 2021-07-23 ## [0.5.1] -- 2021-07-23
## Added ## Added
- Allow user to specify locations as unique identifiers, instead of latitude and longitude (e.g. `us-state:IL` or `2018-us-county:17043`) - Allow user to specify locations as unique identifiers, instead of latitude and longitude (e.g. `us-state:IL` or `2018-us-county:17043`)

@ -1,6 +1,6 @@
JULIA := julia --project=. JULIA := julia --project=.
SRC_FILES := $(wildcard src/*.jl test/*.jl) SRC_FILES := $(wildcard src/*.jl test/*.jl)
VERSION := 0.5 VERSION := dev
all: docs test all: docs test

@ -29,6 +29,8 @@ function _compress(instance::Instance)::Instance
for (emission_name, emission_value) in p.transportation_emissions for (emission_name, emission_value) in p.transportation_emissions
p.transportation_emissions[emission_name] = [mean(emission_value)] p.transportation_emissions[emission_name] = [mean(emission_value)]
end end
p.disposal_limit = [maximum(p.disposal_limit) * T]
p.disposal_cost = [mean(p.disposal_cost)]
end end
# Compress collection centers # Compress collection centers
@ -58,3 +60,42 @@ function _compress(instance::Instance)::Instance
return compressed return compressed
end end
function _slice(instance::Instance, T::UnitRange)::Instance
sliced = deepcopy(instance)
sliced.time = length(T)
for p in sliced.products
p.transportation_cost = p.transportation_cost[T]
p.transportation_energy = p.transportation_energy[T]
for (emission_name, emission_value) in p.transportation_emissions
p.transportation_emissions[emission_name] = emission_value[T]
end
p.disposal_limit = p.disposal_limit[T]
p.disposal_cost = p.disposal_cost[T]
end
for c in sliced.collection_centers
c.amount = c.amount[T]
end
for plant in sliced.plants
plant.energy = plant.energy[T]
for (emission_name, emission_value) in plant.emissions
plant.emissions[emission_name] = emission_value[T]
end
for s in plant.sizes
s.variable_operating_cost = s.variable_operating_cost[T]
s.opening_cost = s.opening_cost[T]
s.fixed_operating_cost = s.fixed_operating_cost[T]
end
for (prod_name, disp_limit) in plant.disposal_limit
plant.disposal_limit[prod_name] = disp_limit[T]
end
for (prod_name, disp_cost) in plant.disposal_cost
plant.disposal_cost[prod_name] = disp_cost[T]
end
end
return sliced
end

@ -24,11 +24,15 @@ function create_vars!(model::JuMP.Model)
(n, t) => @variable( (n, t) => @variable(
model, model,
lower_bound = 0, lower_bound = 0,
upper_bound = n.location.disposal_limit[n.product][t] upper_bound = n.location.disposal_limit[n.product][t],
) for n in values(graph.plant_shipping_nodes), t = 1:T ) for n in values(graph.plant_shipping_nodes), t = 1:T
) )
model[:collection_dispose] = Dict( model[:collection_dispose] = Dict(
(n, t) => @variable(model, lower_bound = 0,) for (n, t) => @variable(
model,
lower_bound = 0,
upper_bound = n.location.amount[t],
) for
n in values(graph.collection_shipping_nodes), t = 1:T n in values(graph.collection_shipping_nodes), t = 1:T
) )
model[:store] = Dict( model[:store] = Dict(
@ -90,7 +94,7 @@ function create_objective_function!(model::JuMP.Model)
# Process node costs # Process node costs
for n in values(graph.process_nodes), t = 1:T for n in values(graph.process_nodes), t = 1:T
# Transportation and variable operating costs # Transportation costs
for a in n.incoming_arcs for a in n.incoming_arcs
c = n.location.input.transportation_cost[t] * a.values["distance"] c = n.location.input.transportation_cost[t] * a.values["distance"]
add_to_expression!(obj, c, model[:flow][a, t]) add_to_expression!(obj, c, model[:flow][a, t])
@ -137,7 +141,6 @@ function create_objective_function!(model::JuMP.Model)
# Plant shipping node costs # Plant shipping node costs
for n in values(graph.plant_shipping_nodes), t = 1:T for n in values(graph.plant_shipping_nodes), t = 1:T
# Disposal costs # Disposal costs
add_to_expression!( add_to_expression!(
obj, obj,
@ -148,7 +151,6 @@ function create_objective_function!(model::JuMP.Model)
# Collection shipping node costs # Collection shipping node costs
for n in values(graph.collection_shipping_nodes), t = 1:T for n in values(graph.collection_shipping_nodes), t = 1:T
# Disposal costs # Disposal costs
add_to_expression!( add_to_expression!(
obj, obj,
@ -170,7 +172,7 @@ function create_shipping_node_constraints!(model::JuMP.Model)
model[:eq_balance][n, t] = @constraint( model[:eq_balance][n, t] = @constraint(
model, model,
sum(model[:flow][a, t] for a in n.outgoing_arcs) == sum(model[:flow][a, t] for a in n.outgoing_arcs) ==
n.location.amount[t] + model[:collection_dispose][n, t] n.location.amount[t] - model[:collection_dispose][n, t],
) )
end end
for prod in model[:instance].products for prod in model[:instance].products

@ -29,29 +29,32 @@ function solve(
instance::Instance; instance::Instance;
optimizer = nothing, optimizer = nothing,
output = nothing, output = nothing,
graph = nothing,
marginal_costs = true, marginal_costs = true,
return_model = false, return_model = false,
) )
milp_optimizer = lp_optimizer = optimizer milp_optimizer = lp_optimizer = optimizer
if optimizer == nothing if optimizer === nothing
milp_optimizer = _get_default_milp_optimizer() milp_optimizer = _get_default_milp_optimizer()
lp_optimizer = _get_default_lp_optimizer() lp_optimizer = _get_default_lp_optimizer()
end end
@info "Building graph..." if graph === nothing
graph = RELOG.build_graph(instance) @info "Building graph..."
_print_graph_stats(instance, graph) graph = RELOG.build_graph(instance)
_print_graph_stats(instance, graph)
end
@info "Building optimization model..." @info "Building optimization model..."
model = RELOG.build_model(instance, graph, milp_optimizer) model = RELOG.build_model(instance, graph, milp_optimizer)
@info "Optimizing MILP..." @info "Optimizing MILP..."
JuMP.optimize!(model) JuMP.optimize!(model)
if !has_values(model) if !has_values(model)
error("No solution available") error("No solution available")
end end
solution = get_solution(model, marginal_costs = false)
if marginal_costs if marginal_costs
@info "Re-optimizing with integer variables fixed..." @info "Re-optimizing with integer variables fixed..."
@ -65,12 +68,15 @@ function solve(
end end
end end
JuMP.optimize!(model) JuMP.optimize!(model)
if has_values(model)
@info "Extracting solution..."
solution = get_solution(model, marginal_costs = true)
else
@warn "Error computing marginal costs. Ignoring."
end
end end
@info "Extracting solution..." if output !== nothing
solution = get_solution(model, marginal_costs = marginal_costs)
if output != nothing
write(solution, output) write(solution, output)
end end
@ -87,7 +93,7 @@ function solve(filename::AbstractString; heuristic = false, kwargs...)
if heuristic && instance.time > 1 if heuristic && instance.time > 1
@info "Solving single-period version..." @info "Solving single-period version..."
compressed = _compress(instance) compressed = _compress(instance)
csol = solve(compressed; output = nothing, marginal_costs = false, kwargs...) csol, model = solve(compressed; output = nothing, marginal_costs = false, return_model = true, kwargs...)
@info "Filtering candidate locations..." @info "Filtering candidate locations..."
selected_pairs = [] selected_pairs = []
for (plant_name, plant_dict) in csol["Plants"] for (plant_name, plant_dict) in csol["Plants"]

Loading…
Cancel
Save