mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-05 23:38:52 -06:00
Reformat source code; set up lint GH Action
This commit is contained in:
27
.github/workflows/lint.yml
vendored
Normal file
27
.github/workflows/lint.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: julia-actions/setup-julia@latest
|
||||||
|
with:
|
||||||
|
version: '1'
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Format check
|
||||||
|
shell: julia --color=yes {0}
|
||||||
|
run: |
|
||||||
|
using Pkg
|
||||||
|
Pkg.add(PackageSpec(name="JuliaFormatter", version="0.14.4"))
|
||||||
|
using JuliaFormatter
|
||||||
|
format("src", verbose=true)
|
||||||
|
format("test", verbose=true)
|
||||||
|
out = String(read(Cmd(`git diff`)))
|
||||||
|
if isempty(out)
|
||||||
|
exit(0)
|
||||||
|
end
|
||||||
|
@error "Some files have not been formatted !!!"
|
||||||
|
write(stdout, out)
|
||||||
|
exit(1)
|
||||||
3
Makefile
3
Makefile
@@ -17,6 +17,9 @@ clean:
|
|||||||
docs:
|
docs:
|
||||||
mkdocs build -d ../docs/$(VERSION)/
|
mkdocs build -d ../docs/$(VERSION)/
|
||||||
|
|
||||||
|
format:
|
||||||
|
julia -e 'using JuliaFormatter; format(["src", "test"], verbose=true);'
|
||||||
|
|
||||||
test: build/test.log
|
test: build/test.log
|
||||||
|
|
||||||
test-watch:
|
test-watch:
|
||||||
|
|||||||
10
src/RELOG.jl
10
src/RELOG.jl
@@ -3,9 +3,9 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
module RELOG
|
module RELOG
|
||||||
include("dotdict.jl")
|
include("dotdict.jl")
|
||||||
include("instance.jl")
|
include("instance.jl")
|
||||||
include("graph.jl")
|
include("graph.jl")
|
||||||
include("model.jl")
|
include("model.jl")
|
||||||
include("reports.jl")
|
include("reports.jl")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ function Base.show(io::IO, d::DotDict)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function recursive_to_dot_dict(el)
|
function recursive_to_dot_dict(el)
|
||||||
if typeof(el) == Dict{String, Any}
|
if typeof(el) == Dict{String,Any}
|
||||||
return DotDict(Dict(Symbol(k) => recursive_to_dot_dict(el[k]) for k in keys(el)))
|
return DotDict(Dict(Symbol(k) => recursive_to_dot_dict(el[k]) for k in keys(el)))
|
||||||
else
|
else
|
||||||
return el
|
return el
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
export recursive_to_dot_dict
|
export recursive_to_dot_dict
|
||||||
|
|||||||
45
src/graph.jl
45
src/graph.jl
@@ -5,14 +5,13 @@
|
|||||||
using Geodesy
|
using Geodesy
|
||||||
|
|
||||||
|
|
||||||
abstract type Node
|
abstract type Node end
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
mutable struct Arc
|
mutable struct Arc
|
||||||
source::Node
|
source::Node
|
||||||
dest::Node
|
dest::Node
|
||||||
values::Dict{String, Float64}
|
values::Dict{String,Float64}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ end
|
|||||||
|
|
||||||
mutable struct ShippingNode <: Node
|
mutable struct ShippingNode <: Node
|
||||||
index::Int
|
index::Int
|
||||||
location::Union{Plant, CollectionCenter}
|
location::Union{Plant,CollectionCenter}
|
||||||
product::Product
|
product::Product
|
||||||
incoming_arcs::Array{Arc}
|
incoming_arcs::Array{Arc}
|
||||||
outgoing_arcs::Array{Arc}
|
outgoing_arcs::Array{Arc}
|
||||||
@@ -47,26 +46,25 @@ function build_graph(instance::Instance)::Graph
|
|||||||
process_nodes = ProcessNode[]
|
process_nodes = ProcessNode[]
|
||||||
plant_shipping_nodes = ShippingNode[]
|
plant_shipping_nodes = ShippingNode[]
|
||||||
collection_shipping_nodes = ShippingNode[]
|
collection_shipping_nodes = ShippingNode[]
|
||||||
|
|
||||||
process_nodes_by_input_product = Dict(product => ProcessNode[]
|
process_nodes_by_input_product =
|
||||||
for product in instance.products)
|
Dict(product => ProcessNode[] for product in instance.products)
|
||||||
shipping_nodes_by_plant = Dict(plant => []
|
shipping_nodes_by_plant = Dict(plant => [] for plant in instance.plants)
|
||||||
for plant in instance.plants)
|
|
||||||
|
|
||||||
# Build collection center shipping nodes
|
# Build collection center shipping nodes
|
||||||
for center in instance.collection_centers
|
for center in instance.collection_centers
|
||||||
node = ShippingNode(next_index, center, center.product, [], [])
|
node = ShippingNode(next_index, center, center.product, [], [])
|
||||||
next_index += 1
|
next_index += 1
|
||||||
push!(collection_shipping_nodes, node)
|
push!(collection_shipping_nodes, node)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Build process and shipping nodes for plants
|
# Build process and shipping nodes for plants
|
||||||
for plant in instance.plants
|
for plant in instance.plants
|
||||||
pn = ProcessNode(next_index, plant, [], [])
|
pn = ProcessNode(next_index, plant, [], [])
|
||||||
next_index += 1
|
next_index += 1
|
||||||
push!(process_nodes, pn)
|
push!(process_nodes, pn)
|
||||||
push!(process_nodes_by_input_product[plant.input], pn)
|
push!(process_nodes_by_input_product[plant.input], pn)
|
||||||
|
|
||||||
for product in keys(plant.output)
|
for product in keys(plant.output)
|
||||||
sn = ShippingNode(next_index, plant, product, [], [])
|
sn = ShippingNode(next_index, plant, product, [], [])
|
||||||
next_index += 1
|
next_index += 1
|
||||||
@@ -74,14 +72,16 @@ function build_graph(instance::Instance)::Graph
|
|||||||
push!(shipping_nodes_by_plant[plant], sn)
|
push!(shipping_nodes_by_plant[plant], sn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Build arcs from collection centers to plants, and from one plant to another
|
# Build arcs from collection centers to plants, and from one plant to another
|
||||||
for source in [collection_shipping_nodes; plant_shipping_nodes]
|
for source in [collection_shipping_nodes; plant_shipping_nodes]
|
||||||
for dest in process_nodes_by_input_product[source.product]
|
for dest in process_nodes_by_input_product[source.product]
|
||||||
distance = calculate_distance(source.location.latitude,
|
distance = calculate_distance(
|
||||||
source.location.longitude,
|
source.location.latitude,
|
||||||
dest.location.latitude,
|
source.location.longitude,
|
||||||
dest.location.longitude)
|
dest.location.latitude,
|
||||||
|
dest.location.longitude,
|
||||||
|
)
|
||||||
values = Dict("distance" => distance)
|
values = Dict("distance" => distance)
|
||||||
arc = Arc(source, dest, values)
|
arc = Arc(source, dest, values)
|
||||||
push!(source.outgoing_arcs, arc)
|
push!(source.outgoing_arcs, arc)
|
||||||
@@ -89,7 +89,7 @@ function build_graph(instance::Instance)::Graph
|
|||||||
push!(arcs, arc)
|
push!(arcs, arc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Build arcs from process nodes to shipping nodes within a plant
|
# Build arcs from process nodes to shipping nodes within a plant
|
||||||
for source in process_nodes
|
for source in process_nodes
|
||||||
plant = source.location
|
plant = source.location
|
||||||
@@ -102,11 +102,8 @@ function build_graph(instance::Instance)::Graph
|
|||||||
push!(arcs, arc)
|
push!(arcs, arc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Graph(process_nodes,
|
return Graph(process_nodes, plant_shipping_nodes, collection_shipping_nodes, arcs)
|
||||||
plant_shipping_nodes,
|
|
||||||
collection_shipping_nodes,
|
|
||||||
arcs)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -122,5 +119,5 @@ end
|
|||||||
function calculate_distance(source_lat, source_lon, dest_lat, dest_lon)::Float64
|
function calculate_distance(source_lat, source_lon, dest_lat, dest_lon)::Float64
|
||||||
x = LLA(source_lat, source_lon, 0.0)
|
x = LLA(source_lat, source_lon, 0.0)
|
||||||
y = LLA(dest_lat, dest_lon, 0.0)
|
y = LLA(dest_lat, dest_lon, 0.0)
|
||||||
return round(distance(x, y) / 1000.0, digits=2)
|
return round(distance(x, y) / 1000.0, digits = 2)
|
||||||
end
|
end
|
||||||
|
|||||||
145
src/instance.jl
145
src/instance.jl
@@ -13,7 +13,7 @@ mutable struct Product
|
|||||||
name::String
|
name::String
|
||||||
transportation_cost::Array{Float64}
|
transportation_cost::Array{Float64}
|
||||||
transportation_energy::Array{Float64}
|
transportation_energy::Array{Float64}
|
||||||
transportation_emissions::Dict{String, Array{Float64}}
|
transportation_emissions::Dict{String,Array{Float64}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -40,14 +40,14 @@ mutable struct Plant
|
|||||||
plant_name::String
|
plant_name::String
|
||||||
location_name::String
|
location_name::String
|
||||||
input::Product
|
input::Product
|
||||||
output::Dict{Product, Float64}
|
output::Dict{Product,Float64}
|
||||||
latitude::Float64
|
latitude::Float64
|
||||||
longitude::Float64
|
longitude::Float64
|
||||||
disposal_limit::Dict{Product, Array{Float64}}
|
disposal_limit::Dict{Product,Array{Float64}}
|
||||||
disposal_cost::Dict{Product, Array{Float64}}
|
disposal_cost::Dict{Product,Array{Float64}}
|
||||||
sizes::Array{PlantSize}
|
sizes::Array{PlantSize}
|
||||||
energy::Array{Float64}
|
energy::Array{Float64}
|
||||||
emissions::Dict{String, Array{Float64}}
|
emissions::Dict{String,Array{Float64}}
|
||||||
storage_limit::Float64
|
storage_limit::Float64
|
||||||
storage_cost::Array{Float64}
|
storage_cost::Array{Float64}
|
||||||
end
|
end
|
||||||
@@ -55,9 +55,9 @@ end
|
|||||||
|
|
||||||
mutable struct Instance
|
mutable struct Instance
|
||||||
time::Int64
|
time::Int64
|
||||||
products::Array{Product, 1}
|
products::Array{Product,1}
|
||||||
collection_centers::Array{CollectionCenter, 1}
|
collection_centers::Array{CollectionCenter,1}
|
||||||
plants::Array{Plant, 1}
|
plants::Array{Plant,1}
|
||||||
building_period::Array{Int64}
|
building_period::Array{Int64}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -88,104 +88,113 @@ function parse(json)::Instance
|
|||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
json_schema = JSON.parsefile("$basedir/schemas/input.json")
|
json_schema = JSON.parsefile("$basedir/schemas/input.json")
|
||||||
validate(json, Schema(json_schema))
|
validate(json, Schema(json_schema))
|
||||||
|
|
||||||
T = json["parameters"]["time horizon (years)"]
|
T = json["parameters"]["time horizon (years)"]
|
||||||
json_schema["definitions"]["TimeSeries"]["minItems"] = T
|
json_schema["definitions"]["TimeSeries"]["minItems"] = T
|
||||||
json_schema["definitions"]["TimeSeries"]["maxItems"] = T
|
json_schema["definitions"]["TimeSeries"]["maxItems"] = T
|
||||||
validate(json, Schema(json_schema))
|
validate(json, Schema(json_schema))
|
||||||
|
|
||||||
building_period = [1]
|
building_period = [1]
|
||||||
if "building period (years)" in keys(json)
|
if "building period (years)" in keys(json)
|
||||||
building_period = json["building period (years)"]
|
building_period = json["building period (years)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
plants = Plant[]
|
plants = Plant[]
|
||||||
products = Product[]
|
products = Product[]
|
||||||
collection_centers = CollectionCenter[]
|
collection_centers = CollectionCenter[]
|
||||||
prod_name_to_product = Dict{String, Product}()
|
prod_name_to_product = Dict{String,Product}()
|
||||||
|
|
||||||
# Create products
|
# Create products
|
||||||
for (product_name, product_dict) in json["products"]
|
for (product_name, product_dict) in json["products"]
|
||||||
cost = product_dict["transportation cost (\$/km/tonne)"]
|
cost = product_dict["transportation cost (\$/km/tonne)"]
|
||||||
energy = zeros(T)
|
energy = zeros(T)
|
||||||
emissions = Dict()
|
emissions = Dict()
|
||||||
|
|
||||||
if "transportation energy (J/km/tonne)" in keys(product_dict)
|
if "transportation energy (J/km/tonne)" in keys(product_dict)
|
||||||
energy = product_dict["transportation energy (J/km/tonne)"]
|
energy = product_dict["transportation energy (J/km/tonne)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
if "transportation emissions (tonne/km/tonne)" in keys(product_dict)
|
if "transportation emissions (tonne/km/tonne)" in keys(product_dict)
|
||||||
emissions = product_dict["transportation emissions (tonne/km/tonne)"]
|
emissions = product_dict["transportation emissions (tonne/km/tonne)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
product = Product(product_name, cost, energy, emissions)
|
product = Product(product_name, cost, energy, emissions)
|
||||||
push!(products, product)
|
push!(products, product)
|
||||||
prod_name_to_product[product_name] = product
|
prod_name_to_product[product_name] = product
|
||||||
|
|
||||||
# Create collection centers
|
# Create collection centers
|
||||||
if "initial amounts" in keys(product_dict)
|
if "initial amounts" in keys(product_dict)
|
||||||
for (center_name, center_dict) in product_dict["initial amounts"]
|
for (center_name, center_dict) in product_dict["initial amounts"]
|
||||||
center = CollectionCenter(length(collection_centers) + 1,
|
center = CollectionCenter(
|
||||||
center_name,
|
length(collection_centers) + 1,
|
||||||
center_dict["latitude (deg)"],
|
center_name,
|
||||||
center_dict["longitude (deg)"],
|
center_dict["latitude (deg)"],
|
||||||
product,
|
center_dict["longitude (deg)"],
|
||||||
center_dict["amount (tonne)"])
|
product,
|
||||||
|
center_dict["amount (tonne)"],
|
||||||
|
)
|
||||||
push!(collection_centers, center)
|
push!(collection_centers, center)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create plants
|
# Create plants
|
||||||
for (plant_name, plant_dict) in json["plants"]
|
for (plant_name, plant_dict) in json["plants"]
|
||||||
input = prod_name_to_product[plant_dict["input"]]
|
input = prod_name_to_product[plant_dict["input"]]
|
||||||
output = Dict()
|
output = Dict()
|
||||||
|
|
||||||
# Plant outputs
|
# Plant outputs
|
||||||
if "outputs (tonne/tonne)" in keys(plant_dict)
|
if "outputs (tonne/tonne)" in keys(plant_dict)
|
||||||
output = Dict(prod_name_to_product[key] => value
|
output = Dict(
|
||||||
for (key, value) in plant_dict["outputs (tonne/tonne)"]
|
prod_name_to_product[key] => value for
|
||||||
if value > 0)
|
(key, value) in plant_dict["outputs (tonne/tonne)"] if value > 0
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
energy = zeros(T)
|
energy = zeros(T)
|
||||||
emissions = Dict()
|
emissions = Dict()
|
||||||
|
|
||||||
if "energy (GJ/tonne)" in keys(plant_dict)
|
if "energy (GJ/tonne)" in keys(plant_dict)
|
||||||
energy = plant_dict["energy (GJ/tonne)"]
|
energy = plant_dict["energy (GJ/tonne)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
if "emissions (tonne/tonne)" in keys(plant_dict)
|
if "emissions (tonne/tonne)" in keys(plant_dict)
|
||||||
emissions = plant_dict["emissions (tonne/tonne)"]
|
emissions = plant_dict["emissions (tonne/tonne)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
for (location_name, location_dict) in plant_dict["locations"]
|
for (location_name, location_dict) in plant_dict["locations"]
|
||||||
sizes = PlantSize[]
|
sizes = PlantSize[]
|
||||||
disposal_limit = Dict(p => [0.0 for t in 1:T] for p in keys(output))
|
disposal_limit = Dict(p => [0.0 for t = 1:T] for p in keys(output))
|
||||||
disposal_cost = Dict(p => [0.0 for t in 1:T] for p in keys(output))
|
disposal_cost = Dict(p => [0.0 for t = 1:T] for p in keys(output))
|
||||||
|
|
||||||
# Disposal
|
# Disposal
|
||||||
if "disposal" in keys(location_dict)
|
if "disposal" in keys(location_dict)
|
||||||
for (product_name, disposal_dict) in location_dict["disposal"]
|
for (product_name, disposal_dict) in location_dict["disposal"]
|
||||||
limit = [1e8 for t in 1:T]
|
limit = [1e8 for t = 1:T]
|
||||||
if "limit (tonne)" in keys(disposal_dict)
|
if "limit (tonne)" in keys(disposal_dict)
|
||||||
limit = disposal_dict["limit (tonne)"]
|
limit = disposal_dict["limit (tonne)"]
|
||||||
end
|
end
|
||||||
disposal_limit[prod_name_to_product[product_name]] = limit
|
disposal_limit[prod_name_to_product[product_name]] = limit
|
||||||
disposal_cost[prod_name_to_product[product_name]] = disposal_dict["cost (\$/tonne)"]
|
disposal_cost[prod_name_to_product[product_name]] =
|
||||||
|
disposal_dict["cost (\$/tonne)"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Capacities
|
# Capacities
|
||||||
for (capacity_name, capacity_dict) in location_dict["capacities (tonne)"]
|
for (capacity_name, capacity_dict) in location_dict["capacities (tonne)"]
|
||||||
push!(sizes, PlantSize(Base.parse(Float64, capacity_name),
|
push!(
|
||||||
capacity_dict["variable operating cost (\$/tonne)"],
|
sizes,
|
||||||
capacity_dict["fixed operating cost (\$)"],
|
PlantSize(
|
||||||
capacity_dict["opening cost (\$)"]))
|
Base.parse(Float64, capacity_name),
|
||||||
|
capacity_dict["variable operating cost (\$/tonne)"],
|
||||||
|
capacity_dict["fixed operating cost (\$)"],
|
||||||
|
capacity_dict["opening cost (\$)"],
|
||||||
|
),
|
||||||
|
)
|
||||||
end
|
end
|
||||||
length(sizes) > 1 || push!(sizes, sizes[1])
|
length(sizes) > 1 || push!(sizes, sizes[1])
|
||||||
sort!(sizes, by = x -> x.capacity)
|
sort!(sizes, by = x -> x.capacity)
|
||||||
|
|
||||||
# Storage
|
# Storage
|
||||||
storage_limit = 0
|
storage_limit = 0
|
||||||
storage_cost = zeros(T)
|
storage_cost = zeros(T)
|
||||||
@@ -194,7 +203,7 @@ function parse(json)::Instance
|
|||||||
storage_limit = storage_dict["limit (tonne)"]
|
storage_limit = storage_dict["limit (tonne)"]
|
||||||
storage_cost = storage_dict["cost (\$/tonne)"]
|
storage_cost = storage_dict["cost (\$/tonne)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Validation: Capacities
|
# Validation: Capacities
|
||||||
if length(sizes) != 2
|
if length(sizes) != 2
|
||||||
throw("At most two capacities are supported")
|
throw("At most two capacities are supported")
|
||||||
@@ -203,28 +212,30 @@ function parse(json)::Instance
|
|||||||
throw("Variable operating costs must be the same for all capacities")
|
throw("Variable operating costs must be the same for all capacities")
|
||||||
end
|
end
|
||||||
|
|
||||||
plant = Plant(length(plants) + 1,
|
plant = Plant(
|
||||||
plant_name,
|
length(plants) + 1,
|
||||||
location_name,
|
plant_name,
|
||||||
input,
|
location_name,
|
||||||
output,
|
input,
|
||||||
location_dict["latitude (deg)"],
|
output,
|
||||||
location_dict["longitude (deg)"],
|
location_dict["latitude (deg)"],
|
||||||
disposal_limit,
|
location_dict["longitude (deg)"],
|
||||||
disposal_cost,
|
disposal_limit,
|
||||||
sizes,
|
disposal_cost,
|
||||||
energy,
|
sizes,
|
||||||
emissions,
|
energy,
|
||||||
storage_limit,
|
emissions,
|
||||||
storage_cost)
|
storage_limit,
|
||||||
|
storage_cost,
|
||||||
|
)
|
||||||
|
|
||||||
push!(plants, plant)
|
push!(plants, plant)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@info @sprintf("%12d collection centers", length(collection_centers))
|
@info @sprintf("%12d collection centers", length(collection_centers))
|
||||||
@info @sprintf("%12d candidate plant locations", length(plants))
|
@info @sprintf("%12d candidate plant locations", length(plants))
|
||||||
|
|
||||||
return Instance(T, products, collection_centers, plants, building_period)
|
return Instance(T, products, collection_centers, plants, building_period)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -242,7 +253,7 @@ function _compress(instance::Instance)::Instance
|
|||||||
compressed = deepcopy(instance)
|
compressed = deepcopy(instance)
|
||||||
compressed.time = 1
|
compressed.time = 1
|
||||||
compressed.building_period = [1]
|
compressed.building_period = [1]
|
||||||
|
|
||||||
# Compress products
|
# Compress products
|
||||||
for p in compressed.products
|
for p in compressed.products
|
||||||
p.transportation_cost = [mean(p.transportation_cost)]
|
p.transportation_cost = [mean(p.transportation_cost)]
|
||||||
@@ -251,12 +262,12 @@ function _compress(instance::Instance)::Instance
|
|||||||
p.transportation_emissions[emission_name] = [mean(emission_value)]
|
p.transportation_emissions[emission_name] = [mean(emission_value)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compress collection centers
|
# Compress collection centers
|
||||||
for c in compressed.collection_centers
|
for c in compressed.collection_centers
|
||||||
c.amount = [maximum(c.amount) * T]
|
c.amount = [maximum(c.amount) * T]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compress plants
|
# Compress plants
|
||||||
for plant in compressed.plants
|
for plant in compressed.plants
|
||||||
plant.energy = [mean(plant.energy)]
|
plant.energy = [mean(plant.energy)]
|
||||||
@@ -276,6 +287,6 @@ function _compress(instance::Instance)::Instance
|
|||||||
plant.disposal_cost[prod_name] = [mean(disp_cost)]
|
plant.disposal_cost[prod_name] = [mean(disp_cost)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return compressed
|
return compressed
|
||||||
end
|
end
|
||||||
|
|||||||
405
src/model.jl
405
src/model.jl
@@ -26,40 +26,51 @@ end
|
|||||||
|
|
||||||
function create_vars!(model::ManufacturingModel)
|
function create_vars!(model::ManufacturingModel)
|
||||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
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.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)
|
|
||||||
|
|
||||||
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.process = Dict((n, t) => @variable(mip,
|
|
||||||
lower_bound = 0)
|
|
||||||
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 in 1:T)
|
|
||||||
|
|
||||||
vars.is_open = Dict((n, t) => @variable(mip, binary=true)
|
vars.flow = Dict((a, t) => @variable(mip, lower_bound = 0) for a in graph.arcs, t = 1:T)
|
||||||
for n in values(graph.process_nodes), t in 1:T)
|
|
||||||
|
|
||||||
vars.capacity = Dict((n, t) => @variable(mip,
|
vars.dispose = Dict(
|
||||||
lower_bound = 0,
|
(n, t) => @variable(
|
||||||
upper_bound = n.location.sizes[2].capacity)
|
mip,
|
||||||
for n in values(graph.process_nodes), t in 1:T)
|
lower_bound = 0,
|
||||||
|
upper_bound = n.location.disposal_limit[n.product][t]
|
||||||
vars.expansion = Dict((n, t) => @variable(mip,
|
) for n in values(graph.plant_shipping_nodes), t = 1:T
|
||||||
lower_bound = 0,
|
)
|
||||||
upper_bound = n.location.sizes[2].capacity -
|
|
||||||
n.location.sizes[1].capacity)
|
vars.store = Dict(
|
||||||
for n in values(graph.process_nodes), t in 1:T)
|
(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 = 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 = 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,
|
||||||
|
lower_bound = 0,
|
||||||
|
upper_bound = n.location.sizes[2].capacity - n.location.sizes[1].capacity
|
||||||
|
) for n in values(graph.process_nodes), t = 1:T
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -68,7 +79,7 @@ function slope_open(plant, t)
|
|||||||
0.0
|
0.0
|
||||||
else
|
else
|
||||||
(plant.sizes[2].opening_cost[t] - plant.sizes[1].opening_cost[t]) /
|
(plant.sizes[2].opening_cost[t] - plant.sizes[1].opening_cost[t]) /
|
||||||
(plant.sizes[2].capacity - plant.sizes[1].capacity)
|
(plant.sizes[2].capacity - plant.sizes[1].capacity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,7 +88,7 @@ function slope_fix_oper_cost(plant, t)
|
|||||||
0.0
|
0.0
|
||||||
else
|
else
|
||||||
(plant.sizes[2].fixed_operating_cost[t] - plant.sizes[1].fixed_operating_cost[t]) /
|
(plant.sizes[2].fixed_operating_cost[t] - plant.sizes[1].fixed_operating_cost[t]) /
|
||||||
(plant.sizes[2].capacity - plant.sizes[1].capacity)
|
(plant.sizes[2].capacity - plant.sizes[1].capacity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -86,111 +97,119 @@ function create_objective_function!(model::ManufacturingModel)
|
|||||||
obj = AffExpr(0.0)
|
obj = AffExpr(0.0)
|
||||||
|
|
||||||
# Process node costs
|
# 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
|
# Transportation and variable operating 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, vars.flow[a, t])
|
add_to_expression!(obj, c, vars.flow[a, t])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Opening costs
|
# Opening costs
|
||||||
add_to_expression!(obj,
|
add_to_expression!(obj, n.location.sizes[1].opening_cost[t], vars.open_plant[n, t])
|
||||||
n.location.sizes[1].opening_cost[t],
|
|
||||||
vars.open_plant[n, t])
|
|
||||||
|
|
||||||
# Fixed operating costs (base)
|
# Fixed operating costs (base)
|
||||||
add_to_expression!(obj,
|
add_to_expression!(
|
||||||
n.location.sizes[1].fixed_operating_cost[t],
|
obj,
|
||||||
vars.is_open[n, t])
|
n.location.sizes[1].fixed_operating_cost[t],
|
||||||
|
vars.is_open[n, t],
|
||||||
|
)
|
||||||
|
|
||||||
# Fixed operating costs (expansion)
|
# Fixed operating costs (expansion)
|
||||||
add_to_expression!(obj,
|
add_to_expression!(obj, slope_fix_oper_cost(n.location, t), vars.expansion[n, t])
|
||||||
slope_fix_oper_cost(n.location, t),
|
|
||||||
vars.expansion[n, t])
|
|
||||||
|
|
||||||
# Processing costs
|
# Processing costs
|
||||||
add_to_expression!(obj,
|
add_to_expression!(
|
||||||
n.location.sizes[1].variable_operating_cost[t],
|
obj,
|
||||||
vars.process[n, t])
|
n.location.sizes[1].variable_operating_cost[t],
|
||||||
|
vars.process[n, t],
|
||||||
|
)
|
||||||
|
|
||||||
# Storage costs
|
# Storage costs
|
||||||
add_to_expression!(obj,
|
add_to_expression!(obj, n.location.storage_cost[t], vars.store[n, t])
|
||||||
n.location.storage_cost[t],
|
|
||||||
vars.store[n, t])
|
|
||||||
|
|
||||||
# Expansion costs
|
# Expansion costs
|
||||||
if t < T
|
if t < T
|
||||||
add_to_expression!(obj,
|
add_to_expression!(
|
||||||
slope_open(n.location, t) - slope_open(n.location, t + 1),
|
obj,
|
||||||
vars.expansion[n, t])
|
slope_open(n.location, t) - slope_open(n.location, t + 1),
|
||||||
|
vars.expansion[n, t],
|
||||||
|
)
|
||||||
else
|
else
|
||||||
add_to_expression!(obj,
|
add_to_expression!(obj, slope_open(n.location, t), vars.expansion[n, t])
|
||||||
slope_open(n.location, t),
|
|
||||||
vars.expansion[n, t])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Shipping node costs
|
# 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
|
# Disposal costs
|
||||||
add_to_expression!(obj,
|
add_to_expression!(obj, n.location.disposal_cost[n.product][t], vars.dispose[n, t])
|
||||||
n.location.disposal_cost[n.product][t],
|
|
||||||
vars.dispose[n, t])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@objective(mip, Min, obj)
|
@objective(mip, Min, obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function create_shipping_node_constraints!(model::ManufacturingModel)
|
function create_shipping_node_constraints!(model::ManufacturingModel)
|
||||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
||||||
eqs = model.eqs
|
eqs = model.eqs
|
||||||
|
|
||||||
eqs.balance = OrderedDict()
|
eqs.balance = OrderedDict()
|
||||||
|
|
||||||
for t in 1:T
|
for t = 1:T
|
||||||
# Collection centers
|
# Collection centers
|
||||||
for n in graph.collection_shipping_nodes
|
for n in graph.collection_shipping_nodes
|
||||||
eqs.balance[n, t] = @constraint(mip,
|
eqs.balance[n, t] = @constraint(
|
||||||
sum(vars.flow[a, t] for a in n.outgoing_arcs)
|
mip,
|
||||||
== n.location.amount[t])
|
sum(vars.flow[a, t] for a in n.outgoing_arcs) == n.location.amount[t]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Plants
|
# Plants
|
||||||
for n in graph.plant_shipping_nodes
|
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.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
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function create_process_node_constraints!(model::ManufacturingModel)
|
function create_process_node_constraints!(model::ManufacturingModel)
|
||||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
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)
|
input_sum = AffExpr(0.0)
|
||||||
for a in n.incoming_arcs
|
for a in n.incoming_arcs
|
||||||
add_to_expression!(input_sum, 1.0, vars.flow[a, t])
|
add_to_expression!(input_sum, 1.0, vars.flow[a, t])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Output amount is implied by amount processed
|
# Output amount is implied by amount processed
|
||||||
for a in n.outgoing_arcs
|
for a in n.outgoing_arcs
|
||||||
@constraint(mip, vars.flow[a, t] == a.values["weight"] * vars.process[n, t])
|
@constraint(mip, vars.flow[a, t] == a.values["weight"] * vars.process[n, t])
|
||||||
end
|
end
|
||||||
|
|
||||||
# If plant is closed, capacity is zero
|
# 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
|
# 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
|
# 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
|
# Can only process up to capacity
|
||||||
@constraint(mip, vars.process[n, t] <= vars.capacity[n, t])
|
@constraint(mip, vars.process[n, t] <= vars.capacity[n, t])
|
||||||
@@ -200,7 +219,7 @@ function create_process_node_constraints!(model::ManufacturingModel)
|
|||||||
@constraint(mip, vars.capacity[n, t] >= vars.capacity[n, t-1])
|
@constraint(mip, vars.capacity[n, t] >= vars.capacity[n, t-1])
|
||||||
@constraint(mip, vars.expansion[n, t] >= vars.expansion[n, t-1])
|
@constraint(mip, vars.expansion[n, t] >= vars.expansion[n, t-1])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Amount received equals amount processed plus stored
|
# Amount received equals amount processed plus stored
|
||||||
store_in = 0
|
store_in = 0
|
||||||
if t > 1
|
if t > 1
|
||||||
@@ -209,18 +228,20 @@ function create_process_node_constraints!(model::ManufacturingModel)
|
|||||||
if t == T
|
if t == T
|
||||||
@constraint(mip, vars.store[n, t] == 0)
|
@constraint(mip, vars.store[n, t] == 0)
|
||||||
end
|
end
|
||||||
@constraint(mip,
|
@constraint(mip, input_sum + store_in == vars.store[n, t] + vars.process[n, t])
|
||||||
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
|
# Plant is currently open if it was already open in the previous time period or
|
||||||
# if it was built just now
|
# if it was built just now
|
||||||
if t > 1
|
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
|
else
|
||||||
@constraint(mip, vars.is_open[n, t] == vars.open_plant[n, t])
|
@constraint(mip, vars.is_open[n, t] == vars.open_plant[n, t])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Plant can only be opened during building period
|
# Plant can only be opened during building period
|
||||||
if t ∉ model.instance.building_period
|
if t ∉ model.instance.building_period
|
||||||
@constraint(mip, vars.open_plant[n, t] == 0)
|
@constraint(mip, vars.open_plant[n, t] == 0)
|
||||||
@@ -231,37 +252,41 @@ end
|
|||||||
default_milp_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
default_milp_optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
default_lp_optimizer = optimizer_with_attributes(Clp.Optimizer, "LogLevel" => 0)
|
default_lp_optimizer = optimizer_with_attributes(Clp.Optimizer, "LogLevel" => 0)
|
||||||
|
|
||||||
function solve(instance::Instance;
|
function solve(
|
||||||
optimizer=nothing,
|
instance::Instance;
|
||||||
output=nothing,
|
optimizer = nothing,
|
||||||
marginal_costs=true,
|
output = nothing,
|
||||||
)
|
marginal_costs = true,
|
||||||
|
)
|
||||||
|
|
||||||
milp_optimizer = lp_optimizer = optimizer
|
milp_optimizer = lp_optimizer = optimizer
|
||||||
if optimizer == nothing
|
if optimizer == nothing
|
||||||
milp_optimizer = default_milp_optimizer
|
milp_optimizer = default_milp_optimizer
|
||||||
lp_optimizer = default_lp_optimizer
|
lp_optimizer = default_lp_optimizer
|
||||||
end
|
end
|
||||||
|
|
||||||
@info "Building graph..."
|
@info "Building graph..."
|
||||||
graph = RELOG.build_graph(instance)
|
graph = RELOG.build_graph(instance)
|
||||||
@info @sprintf(" %12d time periods", instance.time)
|
@info @sprintf(" %12d time periods", instance.time)
|
||||||
@info @sprintf(" %12d process nodes", length(graph.process_nodes))
|
@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 (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 @sprintf(" %12d arcs", length(graph.arcs))
|
||||||
|
|
||||||
@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.mip)
|
JuMP.optimize!(model.mip)
|
||||||
|
|
||||||
if !has_values(model.mip)
|
if !has_values(model.mip)
|
||||||
@warn "No solution available"
|
@warn "No solution available"
|
||||||
return OrderedDict()
|
return OrderedDict()
|
||||||
end
|
end
|
||||||
|
|
||||||
if marginal_costs
|
if marginal_costs
|
||||||
@info "Re-optimizing with integer variables fixed..."
|
@info "Re-optimizing with integer variables fixed..."
|
||||||
all_vars = JuMP.all_variables(model.mip)
|
all_vars = JuMP.all_variables(model.mip)
|
||||||
@@ -275,30 +300,24 @@ function solve(instance::Instance;
|
|||||||
end
|
end
|
||||||
JuMP.optimize!(model.mip)
|
JuMP.optimize!(model.mip)
|
||||||
end
|
end
|
||||||
|
|
||||||
@info "Extracting solution..."
|
@info "Extracting solution..."
|
||||||
solution = get_solution(model, marginal_costs=marginal_costs)
|
solution = get_solution(model, marginal_costs = marginal_costs)
|
||||||
|
|
||||||
if output != nothing
|
if output != nothing
|
||||||
write(solution, output)
|
write(solution, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
return solution
|
return solution
|
||||||
end
|
end
|
||||||
|
|
||||||
function solve(filename::AbstractString;
|
function solve(filename::AbstractString; heuristic = false, kwargs...)
|
||||||
heuristic=false,
|
|
||||||
kwargs...,
|
|
||||||
)
|
|
||||||
@info "Reading $filename..."
|
@info "Reading $filename..."
|
||||||
instance = RELOG.parsefile(filename)
|
instance = RELOG.parsefile(filename)
|
||||||
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;
|
csol = solve(compressed; output = nothing, marginal_costs = false, kwargs...)
|
||||||
output=nothing,
|
|
||||||
marginal_costs=false,
|
|
||||||
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"]
|
||||||
@@ -320,12 +339,11 @@ function solve(filename::AbstractString;
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function get_solution(model::ManufacturingModel;
|
function get_solution(model::ManufacturingModel; marginal_costs = true)
|
||||||
marginal_costs=true,
|
mip, vars, eqs, graph, instance =
|
||||||
)
|
model.mip, model.vars, model.eqs, model.graph, model.instance
|
||||||
mip, vars, eqs, graph, instance = model.mip, model.vars, model.eqs, model.graph, model.instance
|
|
||||||
T = instance.time
|
T = instance.time
|
||||||
|
|
||||||
output = OrderedDict(
|
output = OrderedDict(
|
||||||
"Plants" => OrderedDict(),
|
"Plants" => OrderedDict(),
|
||||||
"Products" => OrderedDict(),
|
"Products" => OrderedDict(),
|
||||||
@@ -339,16 +357,14 @@ function get_solution(model::ManufacturingModel;
|
|||||||
"Storage (\$)" => zeros(T),
|
"Storage (\$)" => zeros(T),
|
||||||
"Total (\$)" => zeros(T),
|
"Total (\$)" => zeros(T),
|
||||||
),
|
),
|
||||||
"Energy" => OrderedDict(
|
"Energy" =>
|
||||||
"Plants (GJ)" => zeros(T),
|
OrderedDict("Plants (GJ)" => zeros(T), "Transportation (GJ)" => zeros(T)),
|
||||||
"Transportation (GJ)" => zeros(T),
|
|
||||||
),
|
|
||||||
"Emissions" => OrderedDict(
|
"Emissions" => OrderedDict(
|
||||||
"Plants (tonne)" => OrderedDict(),
|
"Plants (tonne)" => OrderedDict(),
|
||||||
"Transportation (tonne)" => OrderedDict(),
|
"Transportation (tonne)" => OrderedDict(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
plant_to_process_node = OrderedDict(n.location => n for n in graph.process_nodes)
|
plant_to_process_node = OrderedDict(n.location => n for n in graph.process_nodes)
|
||||||
plant_to_shipping_nodes = OrderedDict()
|
plant_to_shipping_nodes = OrderedDict()
|
||||||
for p in instance.plants
|
for p in instance.plants
|
||||||
@@ -357,13 +373,15 @@ function get_solution(model::ManufacturingModel;
|
|||||||
push!(plant_to_shipping_nodes[p], a.dest)
|
push!(plant_to_shipping_nodes[p], a.dest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Products
|
# Products
|
||||||
if marginal_costs
|
if marginal_costs
|
||||||
for n in graph.collection_shipping_nodes
|
for n in graph.collection_shipping_nodes
|
||||||
location_dict = OrderedDict{Any, Any}(
|
location_dict = OrderedDict{Any,Any}(
|
||||||
"Marginal cost (\$/tonne)" => [round(abs(JuMP.shadow_price(eqs.balance[n, t])), digits=2)
|
"Marginal cost (\$/tonne)" => [
|
||||||
for t in 1:T]
|
round(abs(JuMP.shadow_price(eqs.balance[n, t])), digits = 2) for
|
||||||
|
t = 1:T
|
||||||
|
],
|
||||||
)
|
)
|
||||||
if n.product.name ∉ keys(output["Products"])
|
if n.product.name ∉ keys(output["Products"])
|
||||||
output["Products"][n.product.name] = OrderedDict()
|
output["Products"][n.product.name] = OrderedDict()
|
||||||
@@ -371,83 +389,83 @@ function get_solution(model::ManufacturingModel;
|
|||||||
output["Products"][n.product.name][n.location.name] = location_dict
|
output["Products"][n.product.name][n.location.name] = location_dict
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Plants
|
# Plants
|
||||||
for plant in instance.plants
|
for plant in instance.plants
|
||||||
skip_plant = true
|
skip_plant = true
|
||||||
process_node = plant_to_process_node[plant]
|
process_node = plant_to_process_node[plant]
|
||||||
plant_dict = OrderedDict{Any, Any}(
|
plant_dict = OrderedDict{Any,Any}(
|
||||||
"Input" => OrderedDict(),
|
"Input" => OrderedDict(),
|
||||||
"Output" => OrderedDict(
|
"Output" =>
|
||||||
"Send" => OrderedDict(),
|
OrderedDict("Send" => OrderedDict(), "Dispose" => OrderedDict()),
|
||||||
"Dispose" => OrderedDict(),
|
|
||||||
),
|
|
||||||
"Input product" => plant.input.name,
|
"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(),
|
"Total output" => OrderedDict(),
|
||||||
"Latitude (deg)" => plant.latitude,
|
"Latitude (deg)" => plant.latitude,
|
||||||
"Longitude (deg)" => plant.longitude,
|
"Longitude (deg)" => plant.longitude,
|
||||||
"Capacity (tonne)" => [JuMP.value(vars.capacity[process_node, t])
|
"Capacity (tonne)" =>
|
||||||
for t in 1:T],
|
[JuMP.value(vars.capacity[process_node, t]) for t = 1:T],
|
||||||
"Opening cost (\$)" => [JuMP.value(vars.open_plant[process_node, t]) *
|
"Opening cost (\$)" => [
|
||||||
plant.sizes[1].opening_cost[t]
|
JuMP.value(vars.open_plant[process_node, t]) *
|
||||||
for t in 1: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] +
|
"Fixed operating cost (\$)" => [
|
||||||
JuMP.value(vars.expansion[process_node, t]) *
|
JuMP.value(vars.is_open[process_node, t]) *
|
||||||
slope_fix_oper_cost(plant, t)
|
plant.sizes[1].fixed_operating_cost[t] +
|
||||||
for t in 1:T],
|
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])
|
"Expansion cost (\$)" => [
|
||||||
else
|
(
|
||||||
slope_open(plant, t) * (
|
if t == 1
|
||||||
JuMP.value(vars.expansion[process_node, t]) -
|
slope_open(plant, t) * JuMP.value(vars.expansion[process_node, t])
|
||||||
JuMP.value(vars.expansion[process_node, t - 1])
|
else
|
||||||
)
|
slope_open(plant, t) * (
|
||||||
end)
|
JuMP.value(vars.expansion[process_node, t]) -
|
||||||
for t in 1:T],
|
JuMP.value(vars.expansion[process_node, t-1])
|
||||||
"Process (tonne)" => [JuMP.value(vars.process[process_node, t])
|
)
|
||||||
for t in 1:T],
|
end
|
||||||
"Variable operating cost (\$)" => [JuMP.value(vars.process[process_node, t]) *
|
) for t = 1:T
|
||||||
plant.sizes[1].variable_operating_cost[t]
|
],
|
||||||
for t in 1:T],
|
"Process (tonne)" =>
|
||||||
"Storage (tonne)" => [JuMP.value(vars.store[process_node, t])
|
[JuMP.value(vars.process[process_node, t]) for t = 1:T],
|
||||||
for t in 1:T],
|
"Variable operating cost (\$)" => [
|
||||||
"Storage cost (\$)" => [JuMP.value(vars.store[process_node, t]) *
|
JuMP.value(vars.process[process_node, t]) *
|
||||||
plant.storage_cost[t]
|
plant.sizes[1].variable_operating_cost[t] for t = 1:T
|
||||||
for t in 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"]["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"]["Opening (\$)"] += plant_dict["Opening cost (\$)"]
|
||||||
output["Costs"]["Expansion (\$)"] += plant_dict["Expansion cost (\$)"]
|
output["Costs"]["Expansion (\$)"] += plant_dict["Expansion cost (\$)"]
|
||||||
output["Costs"]["Storage (\$)"] += plant_dict["Storage cost (\$)"]
|
output["Costs"]["Storage (\$)"] += plant_dict["Storage cost (\$)"]
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
for a in process_node.incoming_arcs
|
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
|
if sum(vals) <= 1e-3
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
skip_plant = false
|
skip_plant = false
|
||||||
dict = OrderedDict{Any, Any}(
|
dict = OrderedDict{Any,Any}(
|
||||||
"Amount (tonne)" => vals,
|
"Amount (tonne)" => vals,
|
||||||
"Distance (km)" => a.values["distance"],
|
"Distance (km)" => a.values["distance"],
|
||||||
"Latitude (deg)" => a.source.location.latitude,
|
"Latitude (deg)" => a.source.location.latitude,
|
||||||
"Longitude (deg)" => a.source.location.longitude,
|
"Longitude (deg)" => a.source.location.longitude,
|
||||||
"Transportation cost (\$)" => a.source.product.transportation_cost .*
|
"Transportation cost (\$)" =>
|
||||||
vals .*
|
a.source.product.transportation_cost .* vals .* a.values["distance"],
|
||||||
a.values["distance"],
|
"Transportation energy (J)" =>
|
||||||
"Transportation energy (J)" => vals .*
|
vals .* a.values["distance"] .* a.source.product.transportation_energy,
|
||||||
a.values["distance"] .*
|
|
||||||
a.source.product.transportation_energy,
|
|
||||||
"Emissions (tonne)" => OrderedDict(),
|
"Emissions (tonne)" => OrderedDict(),
|
||||||
)
|
)
|
||||||
emissions_dict = output["Emissions"]["Transportation (tonne)"]
|
emissions_dict = output["Emissions"]["Transportation (tonne)"]
|
||||||
for (em_name, em_values) in a.source.product.transportation_emissions
|
for (em_name, em_values) in a.source.product.transportation_emissions
|
||||||
dict["Emissions (tonne)"][em_name] = em_values .*
|
dict["Emissions (tonne)"][em_name] =
|
||||||
dict["Amount (tonne)"] .*
|
em_values .* dict["Amount (tonne)"] .* a.values["distance"]
|
||||||
a.values["distance"]
|
|
||||||
if em_name ∉ keys(emissions_dict)
|
if em_name ∉ keys(emissions_dict)
|
||||||
emissions_dict[em_name] = zeros(T)
|
emissions_dict[em_name] = zeros(T)
|
||||||
end
|
end
|
||||||
@@ -460,23 +478,25 @@ function get_solution(model::ManufacturingModel;
|
|||||||
plant_name = a.source.location.plant_name
|
plant_name = a.source.location.plant_name
|
||||||
location_name = a.source.location.location_name
|
location_name = a.source.location.location_name
|
||||||
end
|
end
|
||||||
|
|
||||||
if plant_name ∉ keys(plant_dict["Input"])
|
if plant_name ∉ keys(plant_dict["Input"])
|
||||||
plant_dict["Input"][plant_name] = OrderedDict()
|
plant_dict["Input"][plant_name] = OrderedDict()
|
||||||
end
|
end
|
||||||
plant_dict["Input"][plant_name][location_name] = dict
|
plant_dict["Input"][plant_name][location_name] = dict
|
||||||
plant_dict["Total input (tonne)"] += vals
|
plant_dict["Total input (tonne)"] += vals
|
||||||
output["Costs"]["Transportation (\$)"] += dict["Transportation cost (\$)"]
|
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
|
end
|
||||||
|
|
||||||
plant_dict["Energy (GJ)"] = plant_dict["Total input (tonne)"] .* plant.energy
|
plant_dict["Energy (GJ)"] = plant_dict["Total input (tonne)"] .* plant.energy
|
||||||
output["Energy"]["Plants (GJ)"] += plant_dict["Energy (GJ)"]
|
output["Energy"]["Plants (GJ)"] += plant_dict["Energy (GJ)"]
|
||||||
|
|
||||||
plant_dict["Emissions (tonne)"] = OrderedDict()
|
plant_dict["Emissions (tonne)"] = OrderedDict()
|
||||||
emissions_dict = output["Emissions"]["Plants (tonne)"]
|
emissions_dict = output["Emissions"]["Plants (tonne)"]
|
||||||
for (em_name, em_values) in plant.emissions
|
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)
|
if em_name ∉ keys(emissions_dict)
|
||||||
emissions_dict[em_name] = zeros(T)
|
emissions_dict[em_name] = zeros(T)
|
||||||
end
|
end
|
||||||
@@ -489,21 +509,23 @@ function get_solution(model::ManufacturingModel;
|
|||||||
plant_dict["Total output"][product_name] = zeros(T)
|
plant_dict["Total output"][product_name] = zeros(T)
|
||||||
plant_dict["Output"]["Send"][product_name] = product_dict = OrderedDict()
|
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
|
if sum(disposal_amount) > 1e-5
|
||||||
skip_plant = false
|
skip_plant = false
|
||||||
plant_dict["Output"]["Dispose"][product_name] = disposal_dict = OrderedDict()
|
plant_dict["Output"]["Dispose"][product_name] =
|
||||||
disposal_dict["Amount (tonne)"] = [JuMP.value(model.vars.dispose[shipping_node, t])
|
disposal_dict = OrderedDict()
|
||||||
for t in 1:T]
|
disposal_dict["Amount (tonne)"] =
|
||||||
disposal_dict["Cost (\$)"] = [disposal_dict["Amount (tonne)"][t] *
|
[JuMP.value(model.vars.dispose[shipping_node, t]) for t = 1:T]
|
||||||
plant.disposal_cost[shipping_node.product][t]
|
disposal_dict["Cost (\$)"] = [
|
||||||
for t in 1:T]
|
disposal_dict["Amount (tonne)"][t] *
|
||||||
|
plant.disposal_cost[shipping_node.product][t] for t = 1:T
|
||||||
|
]
|
||||||
plant_dict["Total output"][product_name] += disposal_amount
|
plant_dict["Total output"][product_name] += disposal_amount
|
||||||
output["Costs"]["Disposal (\$)"] += disposal_dict["Cost (\$)"]
|
output["Costs"]["Disposal (\$)"] += disposal_dict["Cost (\$)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
for a in shipping_node.outgoing_arcs
|
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
|
if sum(vals) <= 1e-3
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
@@ -517,11 +539,12 @@ function get_solution(model::ManufacturingModel;
|
|||||||
if a.dest.location.plant_name ∉ keys(product_dict)
|
if a.dest.location.plant_name ∉ keys(product_dict)
|
||||||
product_dict[a.dest.location.plant_name] = OrderedDict()
|
product_dict[a.dest.location.plant_name] = OrderedDict()
|
||||||
end
|
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
|
plant_dict["Total output"][product_name] += vals
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if !skip_plant
|
if !skip_plant
|
||||||
if plant.plant_name ∉ keys(output["Plants"])
|
if plant.plant_name ∉ keys(output["Plants"])
|
||||||
output["Plants"][plant.plant_name] = OrderedDict()
|
output["Plants"][plant.plant_name] = OrderedDict()
|
||||||
|
|||||||
255
src/reports.jl
255
src/reports.jl
@@ -27,41 +27,49 @@ function plants_report(solution)::DataFrame
|
|||||||
T = length(solution["Energy"]["Plants (GJ)"])
|
T = length(solution["Energy"]["Plants (GJ)"])
|
||||||
for (plant_name, plant_dict) in solution["Plants"]
|
for (plant_name, plant_dict) in solution["Plants"]
|
||||||
for (location_name, location_dict) in plant_dict
|
for (location_name, location_dict) in plant_dict
|
||||||
for year in 1:T
|
for year = 1:T
|
||||||
capacity = round(location_dict["Capacity (tonne)"][year], digits=2)
|
capacity = round(location_dict["Capacity (tonne)"][year], digits = 2)
|
||||||
received = round(location_dict["Total input (tonne)"][year], digits=2)
|
received = round(location_dict["Total input (tonne)"][year], digits = 2)
|
||||||
processed = round(location_dict["Process (tonne)"][year], digits=2)
|
processed = round(location_dict["Process (tonne)"][year], digits = 2)
|
||||||
in_storage = round(location_dict["Storage (tonne)"][year], digits=2)
|
in_storage = round(location_dict["Storage (tonne)"][year], digits = 2)
|
||||||
utilization_factor = round(processed / capacity * 100.0, digits=2)
|
utilization_factor = round(processed / capacity * 100.0, digits = 2)
|
||||||
energy = round(location_dict["Energy (GJ)"][year], digits=2)
|
energy = round(location_dict["Energy (GJ)"][year], digits = 2)
|
||||||
latitude = round(location_dict["Latitude (deg)"], digits=6)
|
latitude = round(location_dict["Latitude (deg)"], digits = 6)
|
||||||
longitude = round(location_dict["Longitude (deg)"], digits=6)
|
longitude = round(location_dict["Longitude (deg)"], digits = 6)
|
||||||
opening_cost = round(location_dict["Opening cost (\$)"][year], digits=2)
|
opening_cost = round(location_dict["Opening cost (\$)"][year], digits = 2)
|
||||||
expansion_cost = round(location_dict["Expansion cost (\$)"][year], digits=2)
|
expansion_cost =
|
||||||
fixed_cost = round(location_dict["Fixed operating cost (\$)"][year], digits=2)
|
round(location_dict["Expansion cost (\$)"][year], digits = 2)
|
||||||
var_cost = round(location_dict["Variable operating cost (\$)"][year], digits=2)
|
fixed_cost =
|
||||||
storage_cost = round(location_dict["Storage cost (\$)"][year], digits=2)
|
round(location_dict["Fixed operating cost (\$)"][year], digits = 2)
|
||||||
total_cost = round(opening_cost + expansion_cost + fixed_cost +
|
var_cost =
|
||||||
var_cost + storage_cost, digits=2)
|
round(location_dict["Variable operating cost (\$)"][year], digits = 2)
|
||||||
push!(df, [
|
storage_cost = round(location_dict["Storage cost (\$)"][year], digits = 2)
|
||||||
plant_name,
|
total_cost = round(
|
||||||
location_name,
|
opening_cost + expansion_cost + fixed_cost + var_cost + storage_cost,
|
||||||
year,
|
digits = 2,
|
||||||
latitude,
|
)
|
||||||
longitude,
|
push!(
|
||||||
capacity,
|
df,
|
||||||
processed,
|
[
|
||||||
received,
|
plant_name,
|
||||||
in_storage,
|
location_name,
|
||||||
utilization_factor,
|
year,
|
||||||
energy,
|
latitude,
|
||||||
opening_cost,
|
longitude,
|
||||||
expansion_cost,
|
capacity,
|
||||||
fixed_cost,
|
processed,
|
||||||
var_cost,
|
received,
|
||||||
storage_cost,
|
in_storage,
|
||||||
total_cost,
|
utilization_factor,
|
||||||
])
|
energy,
|
||||||
|
opening_cost,
|
||||||
|
expansion_cost,
|
||||||
|
fixed_cost,
|
||||||
|
var_cost,
|
||||||
|
storage_cost,
|
||||||
|
total_cost,
|
||||||
|
],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -84,7 +92,7 @@ function plant_outputs_report(solution)::DataFrame
|
|||||||
for (product_name, amount_produced) in location_dict["Total output"]
|
for (product_name, amount_produced) in location_dict["Total output"]
|
||||||
send_dict = location_dict["Output"]["Send"]
|
send_dict = location_dict["Output"]["Send"]
|
||||||
disposal_dict = location_dict["Output"]["Dispose"]
|
disposal_dict = location_dict["Output"]["Dispose"]
|
||||||
|
|
||||||
sent = zeros(T)
|
sent = zeros(T)
|
||||||
if product_name in keys(send_dict)
|
if product_name in keys(send_dict)
|
||||||
for (dst_plant_name, dst_plant_dict) in send_dict[product_name]
|
for (dst_plant_name, dst_plant_dict) in send_dict[product_name]
|
||||||
@@ -93,28 +101,31 @@ function plant_outputs_report(solution)::DataFrame
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sent = round.(sent, digits=2)
|
sent = round.(sent, digits = 2)
|
||||||
|
|
||||||
disposal_amount = zeros(T)
|
disposal_amount = zeros(T)
|
||||||
disposal_cost = zeros(T)
|
disposal_cost = zeros(T)
|
||||||
if product_name in keys(disposal_dict)
|
if product_name in keys(disposal_dict)
|
||||||
disposal_amount += disposal_dict[product_name]["Amount (tonne)"]
|
disposal_amount += disposal_dict[product_name]["Amount (tonne)"]
|
||||||
disposal_cost += disposal_dict[product_name]["Cost (\$)"]
|
disposal_cost += disposal_dict[product_name]["Cost (\$)"]
|
||||||
end
|
end
|
||||||
disposal_amount = round.(disposal_amount, digits=2)
|
disposal_amount = round.(disposal_amount, digits = 2)
|
||||||
disposal_cost = round.(disposal_cost, digits=2)
|
disposal_cost = round.(disposal_cost, digits = 2)
|
||||||
|
|
||||||
for year in 1:T
|
for year = 1:T
|
||||||
push!(df, [
|
push!(
|
||||||
plant_name,
|
df,
|
||||||
location_name,
|
[
|
||||||
year,
|
plant_name,
|
||||||
product_name,
|
location_name,
|
||||||
round(amount_produced[year], digits=2),
|
year,
|
||||||
sent[year],
|
product_name,
|
||||||
disposal_amount[year],
|
round(amount_produced[year], digits = 2),
|
||||||
disposal_cost[year],
|
sent[year],
|
||||||
])
|
disposal_amount[year],
|
||||||
|
disposal_cost[year],
|
||||||
|
],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -134,14 +145,17 @@ function plant_emissions_report(solution)::DataFrame
|
|||||||
for (plant_name, plant_dict) in solution["Plants"]
|
for (plant_name, plant_dict) in solution["Plants"]
|
||||||
for (location_name, location_dict) in plant_dict
|
for (location_name, location_dict) in plant_dict
|
||||||
for (emission_name, emission_amount) in location_dict["Emissions (tonne)"]
|
for (emission_name, emission_amount) in location_dict["Emissions (tonne)"]
|
||||||
for year in 1:T
|
for year = 1:T
|
||||||
push!(df, [
|
push!(
|
||||||
plant_name,
|
df,
|
||||||
location_name,
|
[
|
||||||
year,
|
plant_name,
|
||||||
emission_name,
|
location_name,
|
||||||
round(emission_amount[year], digits=2),
|
year,
|
||||||
])
|
emission_name,
|
||||||
|
round(emission_amount[year], digits = 2),
|
||||||
|
],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -165,34 +179,49 @@ function transportation_report(solution)::DataFrame
|
|||||||
df."distance (km)" = Float64[]
|
df."distance (km)" = Float64[]
|
||||||
df."amount (tonne)" = Float64[]
|
df."amount (tonne)" = Float64[]
|
||||||
df."amount-distance (tonne-km)" = Float64[]
|
df."amount-distance (tonne-km)" = Float64[]
|
||||||
df."transportation cost (\$)" = Float64[]
|
df."transportation cost (\$)" = Float64[]
|
||||||
df."transportation energy (GJ)" = Float64[]
|
df."transportation energy (GJ)" = Float64[]
|
||||||
|
|
||||||
T = length(solution["Energy"]["Plants (GJ)"])
|
T = length(solution["Energy"]["Plants (GJ)"])
|
||||||
for (dst_plant_name, dst_plant_dict) in solution["Plants"]
|
for (dst_plant_name, dst_plant_dict) in solution["Plants"]
|
||||||
for (dst_location_name, dst_location_dict) in dst_plant_dict
|
for (dst_location_name, dst_location_dict) in dst_plant_dict
|
||||||
for (src_plant_name, src_plant_dict) in dst_location_dict["Input"]
|
for (src_plant_name, src_plant_dict) in dst_location_dict["Input"]
|
||||||
for (src_location_name, src_location_dict) in src_plant_dict
|
for (src_location_name, src_location_dict) in src_plant_dict
|
||||||
for year in 1:T
|
for year = 1:T
|
||||||
push!(df, [
|
push!(
|
||||||
src_plant_name,
|
df,
|
||||||
src_location_name,
|
[
|
||||||
round(src_location_dict["Latitude (deg)"], digits=6),
|
src_plant_name,
|
||||||
round(src_location_dict["Longitude (deg)"], digits=6),
|
src_location_name,
|
||||||
dst_plant_name,
|
round(src_location_dict["Latitude (deg)"], digits = 6),
|
||||||
dst_location_name,
|
round(src_location_dict["Longitude (deg)"], digits = 6),
|
||||||
round(dst_location_dict["Latitude (deg)"], digits=6),
|
dst_plant_name,
|
||||||
round(dst_location_dict["Longitude (deg)"], digits=6),
|
dst_location_name,
|
||||||
dst_location_dict["Input product"],
|
round(dst_location_dict["Latitude (deg)"], digits = 6),
|
||||||
year,
|
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||||
round(src_location_dict["Distance (km)"], digits=2),
|
dst_location_dict["Input product"],
|
||||||
round(src_location_dict["Amount (tonne)"][year], digits=2),
|
year,
|
||||||
round(src_location_dict["Amount (tonne)"][year] *
|
round(src_location_dict["Distance (km)"], digits = 2),
|
||||||
src_location_dict["Distance (km)"],
|
round(
|
||||||
digits=2),
|
src_location_dict["Amount (tonne)"][year],
|
||||||
round(src_location_dict["Transportation cost (\$)"][year], digits=2),
|
digits = 2,
|
||||||
round(src_location_dict["Transportation energy (J)"][year] / 1e9, digits=2),
|
),
|
||||||
])
|
round(
|
||||||
|
src_location_dict["Amount (tonne)"][year] *
|
||||||
|
src_location_dict["Distance (km)"],
|
||||||
|
digits = 2,
|
||||||
|
),
|
||||||
|
round(
|
||||||
|
src_location_dict["Transportation cost (\$)"][year],
|
||||||
|
digits = 2,
|
||||||
|
),
|
||||||
|
round(
|
||||||
|
src_location_dict["Transportation energy (J)"][year] /
|
||||||
|
1e9,
|
||||||
|
digits = 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -217,35 +246,44 @@ function transportation_emissions_report(solution)::DataFrame
|
|||||||
df."distance (km)" = Float64[]
|
df."distance (km)" = Float64[]
|
||||||
df."shipped amount (tonne)" = Float64[]
|
df."shipped amount (tonne)" = Float64[]
|
||||||
df."shipped amount-distance (tonne-km)" = Float64[]
|
df."shipped amount-distance (tonne-km)" = Float64[]
|
||||||
df."emission type" = String[]
|
df."emission type" = String[]
|
||||||
df."emission amount (tonne)" = Float64[]
|
df."emission amount (tonne)" = Float64[]
|
||||||
|
|
||||||
T = length(solution["Energy"]["Plants (GJ)"])
|
T = length(solution["Energy"]["Plants (GJ)"])
|
||||||
for (dst_plant_name, dst_plant_dict) in solution["Plants"]
|
for (dst_plant_name, dst_plant_dict) in solution["Plants"]
|
||||||
for (dst_location_name, dst_location_dict) in dst_plant_dict
|
for (dst_location_name, dst_location_dict) in dst_plant_dict
|
||||||
for (src_plant_name, src_plant_dict) in dst_location_dict["Input"]
|
for (src_plant_name, src_plant_dict) in dst_location_dict["Input"]
|
||||||
for (src_location_name, src_location_dict) in src_plant_dict
|
for (src_location_name, src_location_dict) in src_plant_dict
|
||||||
for (emission_name, emission_amount) in src_location_dict["Emissions (tonne)"]
|
for (emission_name, emission_amount) in
|
||||||
for year in 1:T
|
src_location_dict["Emissions (tonne)"]
|
||||||
push!(df, [
|
for year = 1:T
|
||||||
src_plant_name,
|
push!(
|
||||||
src_location_name,
|
df,
|
||||||
round(src_location_dict["Latitude (deg)"], digits=6),
|
[
|
||||||
round(src_location_dict["Longitude (deg)"], digits=6),
|
src_plant_name,
|
||||||
dst_plant_name,
|
src_location_name,
|
||||||
dst_location_name,
|
round(src_location_dict["Latitude (deg)"], digits = 6),
|
||||||
round(dst_location_dict["Latitude (deg)"], digits=6),
|
round(src_location_dict["Longitude (deg)"], digits = 6),
|
||||||
round(dst_location_dict["Longitude (deg)"], digits=6),
|
dst_plant_name,
|
||||||
dst_location_dict["Input product"],
|
dst_location_name,
|
||||||
year,
|
round(dst_location_dict["Latitude (deg)"], digits = 6),
|
||||||
round(src_location_dict["Distance (km)"], digits=2),
|
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||||
round(src_location_dict["Amount (tonne)"][year], digits=2),
|
dst_location_dict["Input product"],
|
||||||
round(src_location_dict["Amount (tonne)"][year] *
|
year,
|
||||||
src_location_dict["Distance (km)"],
|
round(src_location_dict["Distance (km)"], digits = 2),
|
||||||
digits=2),
|
round(
|
||||||
emission_name,
|
src_location_dict["Amount (tonne)"][year],
|
||||||
round(emission_amount[year], digits=2),
|
digits = 2,
|
||||||
])
|
),
|
||||||
|
round(
|
||||||
|
src_location_dict["Amount (tonne)"][year] *
|
||||||
|
src_location_dict["Distance (km)"],
|
||||||
|
digits = 2,
|
||||||
|
),
|
||||||
|
emission_name,
|
||||||
|
round(emission_amount[year], digits = 2),
|
||||||
|
],
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -262,8 +300,7 @@ function write(solution::AbstractDict, filename::AbstractString)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
write_plants_report(solution, filename) =
|
write_plants_report(solution, filename) = CSV.write(filename, plants_report(solution))
|
||||||
CSV.write(filename, plants_report(solution))
|
|
||||||
|
|
||||||
write_plant_outputs_report(solution, filename) =
|
write_plant_outputs_report(solution, filename) =
|
||||||
CSV.write(filename, plant_outputs_report(solution))
|
CSV.write(filename, plant_outputs_report(solution))
|
||||||
|
|||||||
@@ -9,14 +9,7 @@ using JuMP
|
|||||||
using MathOptInterface
|
using MathOptInterface
|
||||||
using ProgressBars
|
using ProgressBars
|
||||||
|
|
||||||
pkg = [:Cbc,
|
pkg = [:Cbc, :Clp, :Geodesy, :JSON, :JSONSchema, :JuMP, :MathOptInterface, :ProgressBars]
|
||||||
:Clp,
|
|
||||||
:Geodesy,
|
|
||||||
:JSON,
|
|
||||||
:JSONSchema,
|
|
||||||
:JuMP,
|
|
||||||
:MathOptInterface,
|
|
||||||
:ProgressBars]
|
|
||||||
|
|
||||||
@info "Building system image..."
|
@info "Building system image..."
|
||||||
create_sysimage(pkg, sysimage_path="build/sysimage.so")
|
create_sysimage(pkg, sysimage_path = "build/sysimage.so")
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ using RELOG
|
|||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
||||||
graph = RELOG.build_graph(instance)
|
graph = RELOG.build_graph(instance)
|
||||||
process_node_by_location_name = Dict(n.location.location_name => n
|
process_node_by_location_name =
|
||||||
for n in graph.process_nodes)
|
Dict(n.location.location_name => n for n in graph.process_nodes)
|
||||||
|
|
||||||
@test length(graph.plant_shipping_nodes) == 8
|
@test length(graph.plant_shipping_nodes) == 8
|
||||||
@test length(graph.collection_shipping_nodes) == 10
|
@test length(graph.collection_shipping_nodes) == 10
|
||||||
@test length(graph.process_nodes) == 6
|
@test length(graph.process_nodes) == 6
|
||||||
|
|
||||||
node = graph.collection_shipping_nodes[1]
|
node = graph.collection_shipping_nodes[1]
|
||||||
@test node.location.name == "C1"
|
@test node.location.name == "C1"
|
||||||
@test length(node.incoming_arcs) == 0
|
@test length(node.incoming_arcs) == 0
|
||||||
@@ -23,20 +23,19 @@ using RELOG
|
|||||||
@test node.outgoing_arcs[1].dest.location.plant_name == "F1"
|
@test node.outgoing_arcs[1].dest.location.plant_name == "F1"
|
||||||
@test node.outgoing_arcs[1].dest.location.location_name == "L1"
|
@test node.outgoing_arcs[1].dest.location.location_name == "L1"
|
||||||
@test node.outgoing_arcs[1].values["distance"] == 1095.62
|
@test node.outgoing_arcs[1].values["distance"] == 1095.62
|
||||||
|
|
||||||
node = process_node_by_location_name["L1"]
|
node = process_node_by_location_name["L1"]
|
||||||
@test node.location.plant_name == "F1"
|
@test node.location.plant_name == "F1"
|
||||||
@test node.location.location_name == "L1"
|
@test node.location.location_name == "L1"
|
||||||
@test length(node.incoming_arcs) == 10
|
@test length(node.incoming_arcs) == 10
|
||||||
@test length(node.outgoing_arcs) == 2
|
@test length(node.outgoing_arcs) == 2
|
||||||
|
|
||||||
node = process_node_by_location_name["L3"]
|
node = process_node_by_location_name["L3"]
|
||||||
@test node.location.plant_name == "F2"
|
@test node.location.plant_name == "F2"
|
||||||
@test node.location.location_name == "L3"
|
@test node.location.location_name == "L3"
|
||||||
@test length(node.incoming_arcs) == 2
|
@test length(node.incoming_arcs) == 2
|
||||||
@test length(node.outgoing_arcs) == 2
|
@test length(node.outgoing_arcs) == 2
|
||||||
|
|
||||||
@test length(graph.arcs) == 38
|
@test length(graph.arcs) == 38
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ using RELOG
|
|||||||
@testset "load" begin
|
@testset "load" begin
|
||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
||||||
|
|
||||||
centers = instance.collection_centers
|
centers = instance.collection_centers
|
||||||
plants = instance.plants
|
plants = instance.plants
|
||||||
products = instance.products
|
products = instance.products
|
||||||
location_name_to_plant = Dict(p.location_name => p for p in plants)
|
location_name_to_plant = Dict(p.location_name => p for p in plants)
|
||||||
product_name_to_product = Dict(p.name => p for p in products)
|
product_name_to_product = Dict(p.name => p for p in products)
|
||||||
|
|
||||||
@test length(centers) == 10
|
@test length(centers) == 10
|
||||||
@test centers[1].name == "C1"
|
@test centers[1].name == "C1"
|
||||||
@test centers[1].latitude == 7
|
@test centers[1].latitude == 7
|
||||||
@@ -21,7 +21,7 @@ using RELOG
|
|||||||
@test centers[1].longitude == 7
|
@test centers[1].longitude == 7
|
||||||
@test centers[1].amount == [934.56, 934.56]
|
@test centers[1].amount == [934.56, 934.56]
|
||||||
@test centers[1].product.name == "P1"
|
@test centers[1].product.name == "P1"
|
||||||
|
|
||||||
@test length(plants) == 6
|
@test length(plants) == 6
|
||||||
|
|
||||||
plant = location_name_to_plant["L1"]
|
plant = location_name_to_plant["L1"]
|
||||||
@@ -30,7 +30,7 @@ using RELOG
|
|||||||
@test plant.input.name == "P1"
|
@test plant.input.name == "P1"
|
||||||
@test plant.latitude == 0
|
@test plant.latitude == 0
|
||||||
@test plant.longitude == 0
|
@test plant.longitude == 0
|
||||||
|
|
||||||
@test length(plant.sizes) == 2
|
@test length(plant.sizes) == 2
|
||||||
@test plant.sizes[1].capacity == 250
|
@test plant.sizes[1].capacity == 250
|
||||||
@test plant.sizes[1].opening_cost == [500, 500]
|
@test plant.sizes[1].opening_cost == [500, 500]
|
||||||
@@ -40,7 +40,7 @@ using RELOG
|
|||||||
@test plant.sizes[2].opening_cost == [1250, 1250]
|
@test plant.sizes[2].opening_cost == [1250, 1250]
|
||||||
@test plant.sizes[2].fixed_operating_cost == [30, 30]
|
@test plant.sizes[2].fixed_operating_cost == [30, 30]
|
||||||
@test plant.sizes[2].variable_operating_cost == [30, 30]
|
@test plant.sizes[2].variable_operating_cost == [30, 30]
|
||||||
|
|
||||||
p2 = product_name_to_product["P2"]
|
p2 = product_name_to_product["P2"]
|
||||||
p3 = product_name_to_product["P3"]
|
p3 = product_name_to_product["P3"]
|
||||||
@test length(plant.output) == 2
|
@test length(plant.output) == 2
|
||||||
@@ -50,36 +50,36 @@ using RELOG
|
|||||||
@test plant.disposal_limit[p3] == [1, 1]
|
@test plant.disposal_limit[p3] == [1, 1]
|
||||||
@test plant.disposal_cost[p2] == [-10, -10]
|
@test plant.disposal_cost[p2] == [-10, -10]
|
||||||
@test plant.disposal_cost[p3] == [-10, -10]
|
@test plant.disposal_cost[p3] == [-10, -10]
|
||||||
|
|
||||||
plant = location_name_to_plant["L3"]
|
plant = location_name_to_plant["L3"]
|
||||||
@test plant.location_name == "L3"
|
@test plant.location_name == "L3"
|
||||||
@test plant.input.name == "P2"
|
@test plant.input.name == "P2"
|
||||||
@test plant.latitude == 25
|
@test plant.latitude == 25
|
||||||
@test plant.longitude == 65
|
@test plant.longitude == 65
|
||||||
|
|
||||||
@test length(plant.sizes) == 2
|
@test length(plant.sizes) == 2
|
||||||
@test plant.sizes[1].capacity == 1000.0
|
@test plant.sizes[1].capacity == 1000.0
|
||||||
@test plant.sizes[1].opening_cost == [3000, 3000]
|
@test plant.sizes[1].opening_cost == [3000, 3000]
|
||||||
@test plant.sizes[1].fixed_operating_cost == [50, 50]
|
@test plant.sizes[1].fixed_operating_cost == [50, 50]
|
||||||
@test plant.sizes[1].variable_operating_cost == [50, 50]
|
@test plant.sizes[1].variable_operating_cost == [50, 50]
|
||||||
@test plant.sizes[1] == plant.sizes[2]
|
@test plant.sizes[1] == plant.sizes[2]
|
||||||
|
|
||||||
p4 = product_name_to_product["P4"]
|
p4 = product_name_to_product["P4"]
|
||||||
@test plant.output[p3] == 0.05
|
@test plant.output[p3] == 0.05
|
||||||
@test plant.output[p4] == 0.8
|
@test plant.output[p4] == 0.8
|
||||||
@test plant.disposal_limit[p3] == [1e8, 1e8]
|
@test plant.disposal_limit[p3] == [1e8, 1e8]
|
||||||
@test plant.disposal_limit[p4] == [0, 0]
|
@test plant.disposal_limit[p4] == [0, 0]
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "validate timeseries" begin
|
@testset "validate timeseries" begin
|
||||||
@test_throws String RELOG.parsefile("fixtures/s1-wrong-length.json")
|
@test_throws String RELOG.parsefile("fixtures/s1-wrong-length.json")
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "compress" begin
|
@testset "compress" begin
|
||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
instance = RELOG.parsefile("$basedir/../instances/s1.json")
|
||||||
compressed = RELOG._compress(instance)
|
compressed = RELOG._compress(instance)
|
||||||
|
|
||||||
product_name_to_product = Dict(p.name => p for p in compressed.products)
|
product_name_to_product = Dict(p.name => p for p in compressed.products)
|
||||||
location_name_to_facility = Dict()
|
location_name_to_facility = Dict()
|
||||||
for p in compressed.plants
|
for p in compressed.plants
|
||||||
@@ -88,7 +88,7 @@ using RELOG
|
|||||||
for c in compressed.collection_centers
|
for c in compressed.collection_centers
|
||||||
location_name_to_facility[c.name] = c
|
location_name_to_facility[c.name] = c
|
||||||
end
|
end
|
||||||
|
|
||||||
p1 = product_name_to_product["P1"]
|
p1 = product_name_to_product["P1"]
|
||||||
p2 = product_name_to_product["P2"]
|
p2 = product_name_to_product["P2"]
|
||||||
p3 = product_name_to_product["P3"]
|
p3 = product_name_to_product["P3"]
|
||||||
@@ -103,10 +103,10 @@ using RELOG
|
|||||||
@test p1.transportation_energy ≈ [0.115]
|
@test p1.transportation_energy ≈ [0.115]
|
||||||
@test p1.transportation_emissions["CO2"] ≈ [0.051]
|
@test p1.transportation_emissions["CO2"] ≈ [0.051]
|
||||||
@test p1.transportation_emissions["CH4"] ≈ [0.0025]
|
@test p1.transportation_emissions["CH4"] ≈ [0.0025]
|
||||||
|
|
||||||
@test c1.name == "C1"
|
@test c1.name == "C1"
|
||||||
@test c1.amount ≈ [1869.12]
|
@test c1.amount ≈ [1869.12]
|
||||||
|
|
||||||
@test l1.plant_name == "F1"
|
@test l1.plant_name == "F1"
|
||||||
@test l1.location_name == "L1"
|
@test l1.location_name == "L1"
|
||||||
@test l1.energy ≈ [0.115]
|
@test l1.energy ≈ [0.115]
|
||||||
@@ -121,7 +121,6 @@ using RELOG
|
|||||||
@test l1.disposal_limit[p2] ≈ [2.0]
|
@test l1.disposal_limit[p2] ≈ [2.0]
|
||||||
@test l1.disposal_limit[p3] ≈ [2.0]
|
@test l1.disposal_limit[p3] ≈ [2.0]
|
||||||
@test l1.disposal_cost[p2] ≈ [-10.0]
|
@test l1.disposal_cost[p2] ≈ [-10.0]
|
||||||
@test l1.disposal_cost[p3] ≈ [-10.0]
|
@test l1.disposal_cost[p3] ≈ [-10.0]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
model = RELOG.build_model(instance, graph, Cbc.Optimizer)
|
model = RELOG.build_model(instance, graph, Cbc.Optimizer)
|
||||||
set_optimizer_attribute(model.mip, "logLevel", 0)
|
set_optimizer_attribute(model.mip, "logLevel", 0)
|
||||||
|
|
||||||
process_node_by_location_name = Dict(n.location.location_name => n
|
process_node_by_location_name =
|
||||||
for n in graph.process_nodes)
|
Dict(n.location.location_name => n for n in graph.process_nodes)
|
||||||
|
|
||||||
|
shipping_node_by_location_and_product_names = Dict(
|
||||||
|
(n.location.location_name, n.product.name) => n for
|
||||||
|
n in graph.plant_shipping_nodes
|
||||||
|
)
|
||||||
|
|
||||||
shipping_node_by_location_and_product_names = Dict((n.location.location_name, n.product.name) => n
|
|
||||||
for n in graph.plant_shipping_nodes)
|
|
||||||
|
|
||||||
@test length(model.vars.flow) == 76
|
@test length(model.vars.flow) == 76
|
||||||
@test length(model.vars.dispose) == 16
|
@test length(model.vars.dispose) == 16
|
||||||
@test length(model.vars.open_plant) == 12
|
@test length(model.vars.open_plant) == 12
|
||||||
@@ -27,15 +29,15 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
v = model.vars.capacity[l1, 1]
|
v = model.vars.capacity[l1, 1]
|
||||||
@test lower_bound(v) == 0.0
|
@test lower_bound(v) == 0.0
|
||||||
@test upper_bound(v) == 1000.0
|
@test upper_bound(v) == 1000.0
|
||||||
|
|
||||||
v = model.vars.expansion[l1, 1]
|
v = model.vars.expansion[l1, 1]
|
||||||
@test lower_bound(v) == 0.0
|
@test lower_bound(v) == 0.0
|
||||||
@test upper_bound(v) == 750.0
|
@test upper_bound(v) == 750.0
|
||||||
|
|
||||||
v = model.vars.dispose[shipping_node_by_location_and_product_names["L1", "P2"], 1]
|
v = model.vars.dispose[shipping_node_by_location_and_product_names["L1", "P2"], 1]
|
||||||
@test lower_bound(v) == 0.0
|
@test lower_bound(v) == 0.0
|
||||||
@test upper_bound(v) == 1.0
|
@test upper_bound(v) == 1.0
|
||||||
|
|
||||||
# dest = FileFormats.Model(format = FileFormats.FORMAT_LP)
|
# dest = FileFormats.Model(format = FileFormats.FORMAT_LP)
|
||||||
# MOI.copy_to(dest, model.mip)
|
# MOI.copy_to(dest, model.mip)
|
||||||
# MOI.write_to_file(dest, "model.lp")
|
# MOI.write_to_file(dest, "model.lp")
|
||||||
@@ -44,14 +46,14 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
@testset "solve (exact)" begin
|
@testset "solve (exact)" begin
|
||||||
solution_filename_a = tempname()
|
solution_filename_a = tempname()
|
||||||
solution_filename_b = tempname()
|
solution_filename_b = tempname()
|
||||||
solution = RELOG.solve("$(pwd())/../instances/s1.json",
|
solution =
|
||||||
output=solution_filename_a)
|
RELOG.solve("$(pwd())/../instances/s1.json", output = solution_filename_a)
|
||||||
|
|
||||||
@test isfile(solution_filename_a)
|
@test isfile(solution_filename_a)
|
||||||
|
|
||||||
RELOG.write(solution, solution_filename_b)
|
RELOG.write(solution, solution_filename_b)
|
||||||
@test isfile(solution_filename_b)
|
@test isfile(solution_filename_b)
|
||||||
|
|
||||||
@test "Costs" in keys(solution)
|
@test "Costs" in keys(solution)
|
||||||
@test "Fixed operating (\$)" in keys(solution["Costs"])
|
@test "Fixed operating (\$)" in keys(solution["Costs"])
|
||||||
@test "Transportation (\$)" in keys(solution["Costs"])
|
@test "Transportation (\$)" in keys(solution["Costs"])
|
||||||
@@ -64,11 +66,11 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
@test "F3" in keys(solution["Plants"])
|
@test "F3" in keys(solution["Plants"])
|
||||||
@test "F4" in keys(solution["Plants"])
|
@test "F4" in keys(solution["Plants"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@testset "solve (heuristic)" begin
|
@testset "solve (heuristic)" begin
|
||||||
# Should not crash
|
# Should not crash
|
||||||
solution = RELOG.solve("$(pwd())/../instances/s1.json", heuristic=true)
|
solution = RELOG.solve("$(pwd())/../instances/s1.json", heuristic = true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "infeasible solve" begin
|
@testset "infeasible solve" begin
|
||||||
@@ -78,21 +80,21 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
end
|
end
|
||||||
RELOG.solve(RELOG.parse(json))
|
RELOG.solve(RELOG.parse(json))
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "storage" begin
|
@testset "storage" begin
|
||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
filename = "$basedir/fixtures/storage.json"
|
filename = "$basedir/fixtures/storage.json"
|
||||||
instance = RELOG.parsefile(filename)
|
instance = RELOG.parsefile(filename)
|
||||||
@test instance.plants[1].storage_limit == 50.0
|
@test instance.plants[1].storage_limit == 50.0
|
||||||
@test instance.plants[1].storage_cost == [2.0, 1.5, 1.0]
|
@test instance.plants[1].storage_cost == [2.0, 1.5, 1.0]
|
||||||
|
|
||||||
solution = RELOG.solve(filename)
|
solution = RELOG.solve(filename)
|
||||||
plant_dict = solution["Plants"]["mega plant"]["Chicago"]
|
plant_dict = solution["Plants"]["mega plant"]["Chicago"]
|
||||||
@test plant_dict["Variable operating cost (\$)"] == [500.0, 0.0, 100.0]
|
@test plant_dict["Variable operating cost (\$)"] == [500.0, 0.0, 100.0]
|
||||||
@test plant_dict["Process (tonne)"] == [50.0, 0.0, 50.0]
|
@test plant_dict["Process (tonne)"] == [50.0, 0.0, 50.0]
|
||||||
@test plant_dict["Storage (tonne)"] == [50.0, 50.0, 0.0]
|
@test plant_dict["Storage (tonne)"] == [50.0, 50.0, 0.0]
|
||||||
@test plant_dict["Storage cost (\$)"] == [100.0, 75.0, 0.0]
|
@test plant_dict["Storage cost (\$)"] == [100.0, 75.0, 0.0]
|
||||||
|
|
||||||
@test solution["Costs"]["Variable operating (\$)"] == [500.0, 0.0, 100.0]
|
@test solution["Costs"]["Variable operating (\$)"] == [500.0, 0.0, 100.0]
|
||||||
@test solution["Costs"]["Storage (\$)"] == [100.0, 75.0, 0.0]
|
@test solution["Costs"]["Storage (\$)"] == [100.0, 75.0, 0.0]
|
||||||
@test solution["Costs"]["Total (\$)"] == [600.0, 75.0, 100.0]
|
@test solution["Costs"]["Total (\$)"] == [600.0, 75.0, 100.0]
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ load_json_gz(filename) = JSON.parse(GZip.gzopen(filename))
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
@testset "Reports" begin
|
@testset "Reports" begin
|
||||||
# @testset "from fixture" begin
|
# @testset "from fixture" begin
|
||||||
# check(RELOG.write_plants_report, "fixtures/nimh_plants.csv")
|
# check(RELOG.write_plants_report, "fixtures/nimh_plants.csv")
|
||||||
# check(RELOG.write_plant_outputs_report, "fixtures/nimh_plant_outputs.csv")
|
# check(RELOG.write_plant_outputs_report, "fixtures/nimh_plant_outputs.csv")
|
||||||
# check(RELOG.write_plant_emissions_report, "fixtures/nimh_plant_emissions.csv")
|
# check(RELOG.write_plant_emissions_report, "fixtures/nimh_plant_emissions.csv")
|
||||||
# check(RELOG.write_transportation_report, "fixtures/nimh_transportation.csv")
|
# check(RELOG.write_transportation_report, "fixtures/nimh_transportation.csv")
|
||||||
# check(RELOG.write_transportation_emissions_report, "fixtures/nimh_transportation_emissions.csv")
|
# check(RELOG.write_transportation_emissions_report, "fixtures/nimh_transportation_emissions.csv")
|
||||||
# end
|
# end
|
||||||
|
|
||||||
@testset "from solve" begin
|
@testset "from solve" begin
|
||||||
solution = RELOG.solve("$(pwd())/../instances/s1.json")
|
solution = RELOG.solve("$(pwd())/../instances/s1.json")
|
||||||
tmp_filename = tempname()
|
tmp_filename = tempname()
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ using Test
|
|||||||
include("graph_test.jl")
|
include("graph_test.jl")
|
||||||
include("model_test.jl")
|
include("model_test.jl")
|
||||||
include("reports_test.jl")
|
include("reports_test.jl")
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user