Implement global disposal limits

This commit is contained in:
2025-09-16 11:53:32 -05:00
parent 67b1e5fd40
commit e4d4ee1cc8
14 changed files with 126 additions and 79 deletions

View File

@@ -20,9 +20,10 @@ function parse(json)::Instance
error("Invalid distance metric: $distance_metric_str")
end
timeseries(x::Union{Nothing,Number}) = repeat([x], time_horizon)
timeseries(x::Array) = x
timeseries(d::OrderedDict) = OrderedDict(k => timeseries(v) for (k, v) in d)
timeseries(::Nothing; null_val=nothing) = repeat([null_val], time_horizon)
timeseries(x::Number; null_val=nothing) = repeat([x], time_horizon)
timeseries(x::Array; null_val=nothing) = [xi === nothing ? null_val : xi for xi in x]
timeseries(d::OrderedDict; null_val=nothing) = OrderedDict(k => timeseries(v; null_val) for (k, v) in d)
# Read products
products = Product[]
@@ -31,7 +32,8 @@ function parse(json)::Instance
tr_cost = timeseries(pdict["transportation cost (\$/km/tonne)"])
tr_energy = timeseries(pdict["transportation energy (J/km/tonne)"])
tr_emissions = timeseries(pdict["transportation emissions (tonne/km/tonne)"])
prod = Product(; name, tr_cost, tr_energy, tr_emissions)
disposal_limit = timeseries(pdict["disposal limit (tonne)"], null_val=Inf)
prod = Product(; name, tr_cost, tr_energy, tr_emissions, disposal_limit)
push!(products, prod)
products_by_name[name] = prod
end
@@ -51,7 +53,7 @@ function parse(json)::Instance
outputs = [products_by_name[p] for p in cdict["outputs"]]
operating_cost = timeseries(cdict["operating cost (\$)"])
prod_dict(key, null_val) = OrderedDict(
p => [v === nothing ? null_val : v for v in timeseries(cdict[key][p.name])]
p => timeseries(cdict[key][p.name]; null_val)
for p in outputs
)
fixed_output = prod_dict("fixed output (tonne)", 0.0)

View File

@@ -14,6 +14,7 @@ Base.@kwdef struct Product
tr_cost::Vector{Float64}
tr_energy::Vector{Float64}
tr_emissions::OrderedDict{String,Vector{Float64}}
disposal_limit::Vector{Float64}
end
Base.@kwdef struct Center

View File

@@ -298,6 +298,18 @@ function build_model(instance::Instance; optimizer, variable_names::Bool = false
@constraint(model, z_disp[c.name, m.name, t] <= c.disposal_limit[m][t])
end
# Global disposal limit
eq_disposal_limit = _init(model, :eq_disposal_limit)
for m in products, t in T
isfinite(m.disposal_limit[t]) || continue
eq_disposal_limit[m.name, t] = @constraint(
model,
sum(z_disp[p.name, m.name, t] for p in plants if m in keys(p.output)) +
sum(z_disp[c.name, m.name, t] for c in centers if m in c.outputs) <=
m.disposal_limit[t]
)
end
if variable_names
_set_names!(model)
end