mirror of https://github.com/ANL-CEEESA/RELOG.git
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.
234 lines
8.4 KiB
234 lines
8.4 KiB
# RELOG: Reverse Logistics Optimization
|
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
|
# Released under the modified BSD license. See COPYING.md for more details.
|
|
|
|
using DataStructures
|
|
using JSON
|
|
using JSONSchema
|
|
using Printf
|
|
using Statistics
|
|
|
|
function parsefile(path::String)::Instance
|
|
return RELOG.parse(JSON.parsefile(path))
|
|
end
|
|
|
|
function parse(json)::Instance
|
|
basedir = dirname(@__FILE__)
|
|
json_schema = JSON.parsefile("$basedir/../schemas/input.json")
|
|
validate(json, Schema(json_schema))
|
|
|
|
T = json["parameters"]["time horizon (years)"]
|
|
json_schema["definitions"]["TimeSeries"]["minItems"] = T
|
|
json_schema["definitions"]["TimeSeries"]["maxItems"] = T
|
|
validate(json, Schema(json_schema))
|
|
|
|
building_period = [1]
|
|
if "building period (years)" in keys(json["parameters"])
|
|
building_period = json["parameters"]["building period (years)"]
|
|
end
|
|
|
|
distance_metric = EuclideanDistance()
|
|
if "distance metric" in keys(json["parameters"])
|
|
metric_name = json["parameters"]["distance metric"]
|
|
if metric_name == "driving"
|
|
distance_metric = KnnDrivingDistance()
|
|
elseif metric_name == "Euclidean"
|
|
# nop
|
|
else
|
|
error("Unknown distance metric: $metric_name")
|
|
end
|
|
end
|
|
|
|
plants = Plant[]
|
|
products = Product[]
|
|
collection_centers = CollectionCenter[]
|
|
prod_name_to_product = Dict{String,Product}()
|
|
|
|
# Create products
|
|
for (product_name, product_dict) in json["products"]
|
|
cost = product_dict["transportation cost (\$/km/tonne)"]
|
|
energy = zeros(T)
|
|
emissions = Dict()
|
|
disposal_limit = zeros(T)
|
|
disposal_cost = zeros(T)
|
|
acquisition_cost = zeros(T)
|
|
|
|
if "transportation energy (J/km/tonne)" in keys(product_dict)
|
|
energy = product_dict["transportation energy (J/km/tonne)"]
|
|
end
|
|
|
|
if "transportation emissions (tonne/km/tonne)" in keys(product_dict)
|
|
emissions = product_dict["transportation emissions (tonne/km/tonne)"]
|
|
end
|
|
|
|
if "disposal limit (tonne)" in keys(product_dict)
|
|
disposal_limit = product_dict["disposal limit (tonne)"]
|
|
end
|
|
|
|
if "disposal cost (\$/tonne)" in keys(product_dict)
|
|
disposal_cost = product_dict["disposal cost (\$/tonne)"]
|
|
end
|
|
|
|
if "acquisition cost (\$/tonne)" in keys(product_dict)
|
|
acquisition_cost = product_dict["acquisition cost (\$/tonne)"]
|
|
end
|
|
|
|
prod_centers = []
|
|
|
|
product = Product(
|
|
acquisition_cost = acquisition_cost,
|
|
collection_centers = prod_centers,
|
|
disposal_cost = disposal_cost,
|
|
disposal_limit = disposal_limit,
|
|
name = product_name,
|
|
transportation_cost = cost,
|
|
transportation_emissions = emissions,
|
|
transportation_energy = energy,
|
|
)
|
|
push!(products, product)
|
|
prod_name_to_product[product_name] = product
|
|
|
|
# Create collection centers
|
|
if "initial amounts" in keys(product_dict)
|
|
for (center_name, center_dict) in product_dict["initial amounts"]
|
|
if "location" in keys(center_dict)
|
|
region = geodb_query(center_dict["location"])
|
|
center_dict["latitude (deg)"] = region.centroid.lat
|
|
center_dict["longitude (deg)"] = region.centroid.lon
|
|
end
|
|
center = CollectionCenter(
|
|
amount = center_dict["amount (tonne)"],
|
|
index = length(collection_centers) + 1,
|
|
latitude = center_dict["latitude (deg)"],
|
|
longitude = center_dict["longitude (deg)"],
|
|
name = center_name,
|
|
product = product,
|
|
)
|
|
push!(prod_centers, center)
|
|
push!(collection_centers, center)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Create plants
|
|
for (plant_name, plant_dict) in json["plants"]
|
|
input = prod_name_to_product[plant_dict["input"]]
|
|
output = Dict()
|
|
|
|
# Plant outputs
|
|
if "outputs (tonne/tonne)" in keys(plant_dict)
|
|
output = Dict(
|
|
prod_name_to_product[key] => value for
|
|
(key, value) in plant_dict["outputs (tonne/tonne)"] if value > 0
|
|
)
|
|
end
|
|
|
|
energy = zeros(T)
|
|
emissions = Dict()
|
|
|
|
if "energy (GJ/tonne)" in keys(plant_dict)
|
|
energy = plant_dict["energy (GJ/tonne)"]
|
|
end
|
|
|
|
if "emissions (tonne/tonne)" in keys(plant_dict)
|
|
emissions = plant_dict["emissions (tonne/tonne)"]
|
|
end
|
|
|
|
for (location_name, location_dict) in plant_dict["locations"]
|
|
sizes = PlantSize[]
|
|
disposal_limit = Dict(p => [0.0 for t = 1:T] for p in keys(output))
|
|
disposal_cost = Dict(p => [0.0 for t = 1:T] for p in keys(output))
|
|
|
|
# GeoDB
|
|
if "location" in keys(location_dict)
|
|
region = geodb_query(location_dict["location"])
|
|
location_dict["latitude (deg)"] = region.centroid.lat
|
|
location_dict["longitude (deg)"] = region.centroid.lon
|
|
end
|
|
|
|
# Disposal
|
|
if "disposal" in keys(location_dict)
|
|
for (product_name, disposal_dict) in location_dict["disposal"]
|
|
limit = [1e8 for t = 1:T]
|
|
if "limit (tonne)" in keys(disposal_dict)
|
|
limit = disposal_dict["limit (tonne)"]
|
|
end
|
|
disposal_limit[prod_name_to_product[product_name]] = limit
|
|
disposal_cost[prod_name_to_product[product_name]] =
|
|
disposal_dict["cost (\$/tonne)"]
|
|
end
|
|
end
|
|
|
|
# Capacities
|
|
for (capacity_name, capacity_dict) in location_dict["capacities (tonne)"]
|
|
push!(
|
|
sizes,
|
|
PlantSize(
|
|
capacity = Base.parse(Float64, capacity_name),
|
|
fixed_operating_cost = capacity_dict["fixed operating cost (\$)"],
|
|
opening_cost = capacity_dict["opening cost (\$)"],
|
|
variable_operating_cost = capacity_dict["variable operating cost (\$/tonne)"],
|
|
),
|
|
)
|
|
end
|
|
length(sizes) > 1 || push!(sizes, deepcopy(sizes[1]))
|
|
sort!(sizes, by = x -> x.capacity)
|
|
|
|
# Initial capacity
|
|
initial_capacity = 0
|
|
if "initial capacity (tonne)" in keys(location_dict)
|
|
initial_capacity = location_dict["initial capacity (tonne)"]
|
|
end
|
|
|
|
# Storage
|
|
storage_limit = 0
|
|
storage_cost = zeros(T)
|
|
if "storage" in keys(location_dict)
|
|
storage_dict = location_dict["storage"]
|
|
storage_limit = storage_dict["limit (tonne)"]
|
|
storage_cost = storage_dict["cost (\$/tonne)"]
|
|
end
|
|
|
|
# Validation: Capacities
|
|
if length(sizes) != 2
|
|
throw("At most two capacities are supported")
|
|
end
|
|
if sizes[1].variable_operating_cost != sizes[2].variable_operating_cost
|
|
throw("Variable operating costs must be the same for all capacities")
|
|
end
|
|
|
|
plant = Plant(
|
|
disposal_cost = disposal_cost,
|
|
disposal_limit = disposal_limit,
|
|
emissions = emissions,
|
|
energy = energy,
|
|
index = length(plants) + 1,
|
|
initial_capacity = initial_capacity,
|
|
input = input,
|
|
latitude = location_dict["latitude (deg)"],
|
|
location_name = location_name,
|
|
longitude = location_dict["longitude (deg)"],
|
|
output = output,
|
|
plant_name = plant_name,
|
|
sizes = sizes,
|
|
storage_cost = storage_cost,
|
|
storage_limit = storage_limit,
|
|
)
|
|
|
|
push!(plants, plant)
|
|
end
|
|
end
|
|
|
|
@info @sprintf("%12d collection centers", length(collection_centers))
|
|
@info @sprintf("%12d candidate plant locations", length(plants))
|
|
|
|
return Instance(
|
|
time = T,
|
|
products = products,
|
|
collection_centers = collection_centers,
|
|
plants = plants,
|
|
building_period = building_period,
|
|
distance_metric = distance_metric,
|
|
)
|
|
end
|