mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-05 23:38:52 -06:00
Allow product disposal at collection centers
This commit is contained in:
@@ -4,73 +4,132 @@
|
|||||||
},
|
},
|
||||||
"products": {
|
"products": {
|
||||||
"P1": {
|
"P1": {
|
||||||
"transportation cost ($/km/tonne)": [0.015, 0.015],
|
"transportation cost ($/km/tonne)": [
|
||||||
"transportation energy (J/km/tonne)": [0.12, 0.11],
|
0.015,
|
||||||
|
0.015
|
||||||
|
],
|
||||||
|
"transportation energy (J/km/tonne)": [
|
||||||
|
0.12,
|
||||||
|
0.11
|
||||||
|
],
|
||||||
"transportation emissions (tonne/km/tonne)": {
|
"transportation emissions (tonne/km/tonne)": {
|
||||||
"CO2": [0.052, 0.050],
|
"CO2": [
|
||||||
"CH4": [0.003, 0.002]
|
0.052,
|
||||||
|
0.050
|
||||||
|
],
|
||||||
|
"CH4": [
|
||||||
|
0.003,
|
||||||
|
0.002
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"initial amounts": {
|
"initial amounts": {
|
||||||
"C1": {
|
"C1": {
|
||||||
"latitude (deg)": 7.0,
|
"latitude (deg)": 7.0,
|
||||||
"longitude (deg)": 7.0,
|
"longitude (deg)": 7.0,
|
||||||
"amount (tonne)": [934.56, 934.56]
|
"amount (tonne)": [
|
||||||
|
934.56,
|
||||||
|
934.56
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C2": {
|
"C2": {
|
||||||
"latitude (deg)": 7.0,
|
"latitude (deg)": 7.0,
|
||||||
"longitude (deg)": 19.0,
|
"longitude (deg)": 19.0,
|
||||||
"amount (tonne)": [198.95, 198.95]
|
"amount (tonne)": [
|
||||||
|
198.95,
|
||||||
|
198.95
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C3": {
|
"C3": {
|
||||||
"latitude (deg)": 84.0,
|
"latitude (deg)": 84.0,
|
||||||
"longitude (deg)": 76.0,
|
"longitude (deg)": 76.0,
|
||||||
"amount (tonne)": [212.97, 212.97]
|
"amount (tonne)": [
|
||||||
|
212.97,
|
||||||
|
212.97
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C4": {
|
"C4": {
|
||||||
"latitude (deg)": 21.0,
|
"latitude (deg)": 21.0,
|
||||||
"longitude (deg)": 16.0,
|
"longitude (deg)": 16.0,
|
||||||
"amount (tonne)": [352.19, 352.19]
|
"amount (tonne)": [
|
||||||
|
352.19,
|
||||||
|
352.19
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C5": {
|
"C5": {
|
||||||
"latitude (deg)": 32.0,
|
"latitude (deg)": 32.0,
|
||||||
"longitude (deg)": 92.0,
|
"longitude (deg)": 92.0,
|
||||||
"amount (tonne)": [510.33, 510.33]
|
"amount (tonne)": [
|
||||||
|
510.33,
|
||||||
|
510.33
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C6": {
|
"C6": {
|
||||||
"latitude (deg)": 14.0,
|
"latitude (deg)": 14.0,
|
||||||
"longitude (deg)": 62.0,
|
"longitude (deg)": 62.0,
|
||||||
"amount (tonne)": [471.66, 471.66]
|
"amount (tonne)": [
|
||||||
|
471.66,
|
||||||
|
471.66
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C7": {
|
"C7": {
|
||||||
"latitude (deg)": 30.0,
|
"latitude (deg)": 30.0,
|
||||||
"longitude (deg)": 83.0,
|
"longitude (deg)": 83.0,
|
||||||
"amount (tonne)": [785.21, 785.21]
|
"amount (tonne)": [
|
||||||
|
785.21,
|
||||||
|
785.21
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C8": {
|
"C8": {
|
||||||
"latitude (deg)": 35.0,
|
"latitude (deg)": 35.0,
|
||||||
"longitude (deg)": 40.0,
|
"longitude (deg)": 40.0,
|
||||||
"amount (tonne)": [706.17, 706.17]
|
"amount (tonne)": [
|
||||||
|
706.17,
|
||||||
|
706.17
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C9": {
|
"C9": {
|
||||||
"latitude (deg)": 74.0,
|
"latitude (deg)": 74.0,
|
||||||
"longitude (deg)": 52.0,
|
"longitude (deg)": 52.0,
|
||||||
"amount (tonne)": [30.08, 30.08]
|
"amount (tonne)": [
|
||||||
|
30.08,
|
||||||
|
30.08
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"C10": {
|
"C10": {
|
||||||
"latitude (deg)": 22.0,
|
"latitude (deg)": 22.0,
|
||||||
"longitude (deg)": 54.0,
|
"longitude (deg)": 54.0,
|
||||||
"amount (tonne)": [536.52, 536.52]
|
"amount (tonne)": [
|
||||||
}
|
536.52,
|
||||||
|
536.52
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"disposal limit (tonne)": [
|
||||||
|
1.0,
|
||||||
|
1.0
|
||||||
|
],
|
||||||
|
"disposal cost ($/tonne)": [
|
||||||
|
-1000,
|
||||||
|
-1000
|
||||||
|
]
|
||||||
|
},
|
||||||
"P2": {
|
"P2": {
|
||||||
"transportation cost ($/km/tonne)": [0.02, 0.02]
|
"transportation cost ($/km/tonne)": [
|
||||||
|
0.02,
|
||||||
|
0.02
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"P3": {
|
"P3": {
|
||||||
"transportation cost ($/km/tonne)": [0.0125, 0.0125]
|
"transportation cost ($/km/tonne)": [
|
||||||
|
0.0125,
|
||||||
|
0.0125
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"P4": {
|
"P4": {
|
||||||
"transportation cost ($/km/tonne)": [0.0175, 0.0175]
|
"transportation cost ($/km/tonne)": [
|
||||||
|
0.0175,
|
||||||
|
0.0175
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plants": {
|
"plants": {
|
||||||
@@ -80,10 +139,19 @@
|
|||||||
"P2": 0.2,
|
"P2": 0.2,
|
||||||
"P3": 0.5
|
"P3": 0.5
|
||||||
},
|
},
|
||||||
"energy (GJ/tonne)": [0.12, 0.11],
|
"energy (GJ/tonne)": [
|
||||||
|
0.12,
|
||||||
|
0.11
|
||||||
|
],
|
||||||
"emissions (tonne/tonne)": {
|
"emissions (tonne/tonne)": {
|
||||||
"CO2": [0.052, 0.050],
|
"CO2": [
|
||||||
"CH4": [0.003, 0.002]
|
0.052,
|
||||||
|
0.050
|
||||||
|
],
|
||||||
|
"CH4": [
|
||||||
|
0.003,
|
||||||
|
0.002
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"locations": {
|
"locations": {
|
||||||
"L1": {
|
"L1": {
|
||||||
@@ -91,24 +159,54 @@
|
|||||||
"longitude (deg)": 0.0,
|
"longitude (deg)": 0.0,
|
||||||
"disposal": {
|
"disposal": {
|
||||||
"P2": {
|
"P2": {
|
||||||
"cost ($/tonne)": [-10.0, -10.0],
|
"cost ($/tonne)": [
|
||||||
"limit (tonne)": [1.0, 1.0]
|
-10.0,
|
||||||
|
-10.0
|
||||||
|
],
|
||||||
|
"limit (tonne)": [
|
||||||
|
1.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"P3": {
|
"P3": {
|
||||||
"cost ($/tonne)": [-10.0, -10.0],
|
"cost ($/tonne)": [
|
||||||
"limit (tonne)": [1.0, 1.0]
|
-10.0,
|
||||||
|
-10.0
|
||||||
|
],
|
||||||
|
"limit (tonne)": [
|
||||||
|
1.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"250.0": {
|
"250.0": {
|
||||||
"opening cost ($)": [500.0, 500.0],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [30.0, 30.0],
|
500.0,
|
||||||
"variable operating cost ($/tonne)": [30.0, 30.0]
|
500.0
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
30.0,
|
||||||
|
30.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
30.0,
|
||||||
|
30.0
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"1000.0": {
|
"1000.0": {
|
||||||
"opening cost ($)": [1250.0, 1250.0],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [30.0, 30.0],
|
1250.0,
|
||||||
"variable operating cost ($/tonne)": [30.0, 30.0]
|
1250.0
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
30.0,
|
||||||
|
30.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
30.0,
|
||||||
|
30.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -117,14 +215,32 @@
|
|||||||
"longitude (deg)": 0.5,
|
"longitude (deg)": 0.5,
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"0.0": {
|
"0.0": {
|
||||||
"opening cost ($)": [1000, 1000],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [50.0, 50.0],
|
1000,
|
||||||
"variable operating cost ($/tonne)": [50.0, 50.0]
|
1000
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"10000.0": {
|
"10000.0": {
|
||||||
"opening cost ($)": [10000, 10000],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [50.0, 50.0],
|
10000,
|
||||||
"variable operating cost ($/tonne)": [50.0, 50.0]
|
10000
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,14 +258,26 @@
|
|||||||
"longitude (deg)": 65.0,
|
"longitude (deg)": 65.0,
|
||||||
"disposal": {
|
"disposal": {
|
||||||
"P3": {
|
"P3": {
|
||||||
"cost ($/tonne)": [100.0, 100.0]
|
"cost ($/tonne)": [
|
||||||
|
100.0,
|
||||||
|
100.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"1000.0": {
|
"1000.0": {
|
||||||
"opening cost ($)": [3000, 3000],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [50.0, 50.0],
|
3000,
|
||||||
"variable operating cost ($/tonne)": [50.0, 50.0]
|
3000
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -158,9 +286,18 @@
|
|||||||
"longitude (deg)": 0.20,
|
"longitude (deg)": 0.20,
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"10000": {
|
"10000": {
|
||||||
"opening cost ($)": [3000, 3000],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [50.0, 50.0],
|
3000,
|
||||||
"variable operating cost ($/tonne)": [50.0, 50.0]
|
3000
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
50.0,
|
||||||
|
50.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,9 +311,18 @@
|
|||||||
"longitude (deg)": 100.0,
|
"longitude (deg)": 100.0,
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"15000": {
|
"15000": {
|
||||||
"opening cost ($)": [0.0, 0.0],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [0.0, 0.0],
|
0.0,
|
||||||
"variable operating cost ($/tonne)": [-15.0, -15.0]
|
0.0
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
-15.0,
|
||||||
|
-15.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,9 +336,18 @@
|
|||||||
"longitude (deg)": 50.0,
|
"longitude (deg)": 50.0,
|
||||||
"capacities (tonne)": {
|
"capacities (tonne)": {
|
||||||
"10000": {
|
"10000": {
|
||||||
"opening cost ($)": [0.0, 0.0],
|
"opening cost ($)": [
|
||||||
"fixed operating cost ($)": [0.0, 0.0],
|
0.0,
|
||||||
"variable operating cost ($/tonne)": [-15.0, -15.0]
|
0.0
|
||||||
|
],
|
||||||
|
"fixed operating cost ($)": [
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"variable operating cost ($/tonne)": [
|
||||||
|
-15.0,
|
||||||
|
-15.0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ The **products** section describes all products and subproducts in the simulatio
|
|||||||
|`transportation energy (J/km/tonne)` | The energy required to transport this product. Must be a time series. Optional.
|
|`transportation energy (J/km/tonne)` | The energy required to transport this product. Must be a time series. Optional.
|
||||||
|`transportation emissions (tonne/km/tonne)` | A dictionary mapping the name of each greenhouse gas, produced to transport one tonne of this product along one kilometer, to the amount of gas produced (in tonnes). Must be a time series. Optional.
|
|`transportation emissions (tonne/km/tonne)` | A dictionary mapping the name of each greenhouse gas, produced to transport one tonne of this product along one kilometer, to the amount of gas produced (in tonnes). Must be a time series. Optional.
|
||||||
|`initial amounts` | A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted. Must be a time series.
|
|`initial amounts` | A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted. Must be a time series.
|
||||||
|
| `disposal limit (tonne)` | Total amount of product that can be disposed of across all collection centers. If omitted, all product must be processed. This parameter has no effect on product disposal at plants.
|
||||||
|
| `disposal cost ($/tonne)` | Cost of disposing one tonne of this product at a collection center. If omitted, defaults to zero. This parameter has no effect on product disposal costs at plants.
|
||||||
|
|
||||||
Each product may have some amount available at the beginning of each time period. In this case, the key `initial amounts` maps to a dictionary with the following keys:
|
Each product may have some amount available at the beginning of each time period. In this case, the key `initial amounts` maps to a dictionary with the following keys:
|
||||||
|
|
||||||
@@ -73,7 +75,9 @@ Each product may have some amount available at the beginning of each time period
|
|||||||
"transportation emissions (tonne/km/tonne)": {
|
"transportation emissions (tonne/km/tonne)": {
|
||||||
"CO2": [0.052, 0.050],
|
"CO2": [0.052, 0.050],
|
||||||
"CH4": [0.003, 0.002]
|
"CH4": [0.003, 0.002]
|
||||||
}
|
},
|
||||||
|
"disposal cost ($/tonne)": [-10.0, -12.0],
|
||||||
|
"disposal limit (tonne)": [1.0, 1.0],
|
||||||
},
|
},
|
||||||
"P2": {
|
"P2": {
|
||||||
"transportation cost ($/km/tonne)": [0.022, 0.020]
|
"transportation cost ($/km/tonne)": [0.022, 0.020]
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ Report showing primary product amounts, locations and marginal costs. Generated
|
|||||||
| `longitude (deg)` | Longitude of the collection center.
|
| `longitude (deg)` | Longitude of the collection center.
|
||||||
| `year` | What year this row corresponds to. This reports includes one row for each year.
|
| `year` | What year this row corresponds to. This reports includes one row for each year.
|
||||||
| `amount (tonne)` | Amount of product available at this collection center.
|
| `amount (tonne)` | Amount of product available at this collection center.
|
||||||
|
| `amount disposed (tonne)` | Amount of product disposed of at this collection center.
|
||||||
| `marginal cost ($/tonne)` | Cost to process one additional tonne of this product coming from this collection center.
|
| `marginal cost ($/tonne)` | Cost to process one additional tonne of this product coming from this collection center.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ function build_graph(instance::Instance)::Graph
|
|||||||
collection_shipping_nodes = ShippingNode[]
|
collection_shipping_nodes = ShippingNode[]
|
||||||
|
|
||||||
name_to_process_node_map = Dict{Tuple{AbstractString,AbstractString},ProcessNode}()
|
name_to_process_node_map = Dict{Tuple{AbstractString,AbstractString},ProcessNode}()
|
||||||
|
collection_center_to_node = Dict()
|
||||||
|
|
||||||
process_nodes_by_input_product =
|
process_nodes_by_input_product =
|
||||||
Dict(product => ProcessNode[] for product in instance.products)
|
Dict(product => ProcessNode[] for product in instance.products)
|
||||||
@@ -27,6 +28,7 @@ function build_graph(instance::Instance)::Graph
|
|||||||
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
|
||||||
|
collection_center_to_node[center] = node
|
||||||
push!(collection_shipping_nodes, node)
|
push!(collection_shipping_nodes, node)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ function build_graph(instance::Instance)::Graph
|
|||||||
collection_shipping_nodes,
|
collection_shipping_nodes,
|
||||||
arcs,
|
arcs,
|
||||||
name_to_process_node_map,
|
name_to_process_node_map,
|
||||||
|
collection_center_to_node,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ mutable struct Graph
|
|||||||
collection_shipping_nodes::Vector{ShippingNode}
|
collection_shipping_nodes::Vector{ShippingNode}
|
||||||
arcs::Vector{Arc}
|
arcs::Vector{Arc}
|
||||||
name_to_process_node_map::Dict{Tuple{AbstractString,AbstractString},ProcessNode}
|
name_to_process_node_map::Dict{Tuple{AbstractString,AbstractString},ProcessNode}
|
||||||
|
collection_center_to_node::Dict{CollectionCenter,ShippingNode}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, instance::Graph)
|
function Base.show(io::IO, instance::Graph)
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ function parse(json)::Instance
|
|||||||
cost = product_dict["transportation cost (\$/km/tonne)"]
|
cost = product_dict["transportation cost (\$/km/tonne)"]
|
||||||
energy = zeros(T)
|
energy = zeros(T)
|
||||||
emissions = Dict()
|
emissions = Dict()
|
||||||
|
disposal_limit = zeros(T)
|
||||||
|
disposal_cost = zeros(T)
|
||||||
|
|
||||||
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)"]
|
||||||
@@ -46,7 +48,25 @@ function parse(json)::Instance
|
|||||||
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)
|
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
|
||||||
|
|
||||||
|
prod_centers = []
|
||||||
|
|
||||||
|
product = Product(
|
||||||
|
product_name,
|
||||||
|
cost,
|
||||||
|
energy,
|
||||||
|
emissions,
|
||||||
|
disposal_limit,
|
||||||
|
disposal_cost,
|
||||||
|
prod_centers,
|
||||||
|
)
|
||||||
push!(products, product)
|
push!(products, product)
|
||||||
prod_name_to_product[product_name] = product
|
prod_name_to_product[product_name] = product
|
||||||
|
|
||||||
@@ -66,6 +86,7 @@ function parse(json)::Instance
|
|||||||
product,
|
product,
|
||||||
center_dict["amount (tonne)"],
|
center_dict["amount (tonne)"],
|
||||||
)
|
)
|
||||||
|
push!(prod_centers, center)
|
||||||
push!(collection_centers, center)
|
push!(collection_centers, center)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ mutable struct Product
|
|||||||
transportation_cost::Vector{Float64}
|
transportation_cost::Vector{Float64}
|
||||||
transportation_energy::Vector{Float64}
|
transportation_energy::Vector{Float64}
|
||||||
transportation_emissions::Dict{String,Vector{Float64}}
|
transportation_emissions::Dict{String,Vector{Float64}}
|
||||||
|
disposal_limit::Vector{Float64}
|
||||||
|
disposal_cost::Vector{Float64}
|
||||||
|
collection_centers::Vector
|
||||||
end
|
end
|
||||||
|
|
||||||
mutable struct CollectionCenter
|
mutable struct CollectionCenter
|
||||||
|
|||||||
@@ -20,13 +20,17 @@ function create_vars!(model::JuMP.Model)
|
|||||||
graph, T = model[:graph], model[:instance].time
|
graph, T = model[:graph], model[:instance].time
|
||||||
model[:flow] =
|
model[:flow] =
|
||||||
Dict((a, t) => @variable(model, lower_bound = 0) for a in graph.arcs, t = 1:T)
|
Dict((a, t) => @variable(model, lower_bound = 0) for a in graph.arcs, t = 1:T)
|
||||||
model[:dispose] = Dict(
|
model[:plant_dispose] = Dict(
|
||||||
(n, t) => @variable(
|
(n, t) => @variable(
|
||||||
model,
|
model,
|
||||||
lower_bound = 0,
|
lower_bound = 0,
|
||||||
upper_bound = n.location.disposal_limit[n.product][t]
|
upper_bound = n.location.disposal_limit[n.product][t]
|
||||||
) for n in values(graph.plant_shipping_nodes), t = 1:T
|
) for n in values(graph.plant_shipping_nodes), t = 1:T
|
||||||
)
|
)
|
||||||
|
model[:collection_dispose] = Dict(
|
||||||
|
(n, t) => @variable(model, lower_bound = 0,) for
|
||||||
|
n in values(graph.collection_shipping_nodes), t = 1:T
|
||||||
|
)
|
||||||
model[:store] = Dict(
|
model[:store] = Dict(
|
||||||
(n, t) =>
|
(n, t) =>
|
||||||
@variable(model, lower_bound = 0, upper_bound = n.location.storage_limit)
|
@variable(model, lower_bound = 0, upper_bound = n.location.storage_limit)
|
||||||
@@ -131,14 +135,25 @@ function create_objective_function!(model::JuMP.Model)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Shipping node costs
|
# Plant shipping node costs
|
||||||
for n in values(graph.plant_shipping_nodes), t = 1:T
|
for n in values(graph.plant_shipping_nodes), t = 1:T
|
||||||
|
|
||||||
# Disposal costs
|
# Disposal costs
|
||||||
add_to_expression!(
|
add_to_expression!(
|
||||||
obj,
|
obj,
|
||||||
n.location.disposal_cost[n.product][t],
|
n.location.disposal_cost[n.product][t],
|
||||||
model[:dispose][n, t],
|
model[:plant_dispose][n, t],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Collection shipping node costs
|
||||||
|
for n in values(graph.collection_shipping_nodes), t = 1:T
|
||||||
|
|
||||||
|
# Disposal costs
|
||||||
|
add_to_expression!(
|
||||||
|
obj,
|
||||||
|
n.location.product.disposal_cost[t],
|
||||||
|
model[:collection_dispose][n, t],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -154,16 +169,29 @@ function create_shipping_node_constraints!(model::JuMP.Model)
|
|||||||
for n in graph.collection_shipping_nodes
|
for n in graph.collection_shipping_nodes
|
||||||
model[:eq_balance][n, t] = @constraint(
|
model[:eq_balance][n, t] = @constraint(
|
||||||
model,
|
model,
|
||||||
sum(model[:flow][a, t] for a in n.outgoing_arcs) == n.location.amount[t]
|
sum(model[:flow][a, t] for a in n.outgoing_arcs) ==
|
||||||
|
n.location.amount[t] + model[:collection_dispose][n, t]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
for prod in model[:instance].products
|
||||||
|
if isempty(prod.collection_centers)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
expr = AffExpr()
|
||||||
|
for center in prod.collection_centers
|
||||||
|
n = graph.collection_center_to_node[center]
|
||||||
|
add_to_expression!(expr, model[:collection_dispose][n, t])
|
||||||
|
end
|
||||||
|
@constraint(model, expr <= prod.disposal_limit[t])
|
||||||
|
end
|
||||||
|
|
||||||
# Plants
|
# Plants
|
||||||
for n in graph.plant_shipping_nodes
|
for n in graph.plant_shipping_nodes
|
||||||
@constraint(
|
@constraint(
|
||||||
model,
|
model,
|
||||||
sum(model[:flow][a, t] for a in n.incoming_arcs) ==
|
sum(model[:flow][a, t] for a in n.incoming_arcs) ==
|
||||||
sum(model[:flow][a, t] for a in n.outgoing_arcs) + model[:dispose][n, t]
|
sum(model[:flow][a, t] for a in n.outgoing_arcs) +
|
||||||
|
model[:plant_dispose][n, t]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,22 +39,25 @@ function get_solution(model::JuMP.Model; marginal_costs = true)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Products
|
# Products
|
||||||
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(model[:eq_balance][n, t])), digits = 2) for t = 1:T
|
|
||||||
],
|
|
||||||
"Latitude (deg)" => n.location.latitude,
|
"Latitude (deg)" => n.location.latitude,
|
||||||
"Longitude (deg)" => n.location.longitude,
|
"Longitude (deg)" => n.location.longitude,
|
||||||
"Amount (tonne)" => n.location.amount,
|
"Amount (tonne)" => n.location.amount,
|
||||||
|
"Dispose (tonne)" =>
|
||||||
|
[JuMP.value(model[:collection_dispose][n, t]) for t = 1:T],
|
||||||
)
|
)
|
||||||
|
if marginal_costs
|
||||||
|
location_dict["Marginal cost (\$/tonne)"] = [
|
||||||
|
round(abs(JuMP.shadow_price(model[:eq_balance][n, t])), digits = 2) for
|
||||||
|
t = 1:T
|
||||||
|
]
|
||||||
|
end
|
||||||
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()
|
||||||
end
|
end
|
||||||
output["Products"][n.product.name][n.location.name] = location_dict
|
output["Products"][n.product.name][n.location.name] = location_dict
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Plants
|
# Plants
|
||||||
for plant in instance.plants
|
for plant in instance.plants
|
||||||
@@ -178,13 +181,14 @@ function get_solution(model::JuMP.Model; marginal_costs = true)
|
|||||||
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(model[:dispose][shipping_node, t]) for t = 1:T]
|
disposal_amount =
|
||||||
|
[JuMP.value(model[:plant_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] =
|
plant_dict["Output"]["Dispose"][product_name] =
|
||||||
disposal_dict = OrderedDict()
|
disposal_dict = OrderedDict()
|
||||||
disposal_dict["Amount (tonne)"] =
|
disposal_dict["Amount (tonne)"] =
|
||||||
[JuMP.value(model[:dispose][shipping_node, t]) for t = 1:T]
|
[JuMP.value(model[:plant_dispose][shipping_node, t]) for t = 1:T]
|
||||||
disposal_dict["Cost (\$)"] = [
|
disposal_dict["Cost (\$)"] = [
|
||||||
disposal_dict["Amount (tonne)"][t] *
|
disposal_dict["Amount (tonne)"][t] *
|
||||||
plant.disposal_cost[shipping_node.product][t] for t = 1:T
|
plant.disposal_cost[shipping_node.product][t] for t = 1:T
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ function products_report(solution; marginal_costs = true)::DataFrame
|
|||||||
df."longitude (deg)" = Float64[]
|
df."longitude (deg)" = Float64[]
|
||||||
df."year" = Int[]
|
df."year" = Int[]
|
||||||
df."amount (tonne)" = Float64[]
|
df."amount (tonne)" = Float64[]
|
||||||
|
df."amount disposed (tonne)" = Float64[]
|
||||||
df."marginal cost (\$/tonne)" = Float64[]
|
df."marginal cost (\$/tonne)" = Float64[]
|
||||||
T = length(solution["Energy"]["Plants (GJ)"])
|
T = length(solution["Energy"]["Plants (GJ)"])
|
||||||
for (prod_name, prod_dict) in solution["Products"]
|
for (prod_name, prod_dict) in solution["Products"]
|
||||||
@@ -22,6 +23,7 @@ function products_report(solution; marginal_costs = true)::DataFrame
|
|||||||
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)
|
||||||
amount = location_dict["Amount (tonne)"][year]
|
amount = location_dict["Amount (tonne)"][year]
|
||||||
|
amount_disposed = location_dict["Dispose (tonne)"][year]
|
||||||
push!(
|
push!(
|
||||||
df,
|
df,
|
||||||
[
|
[
|
||||||
@@ -32,6 +34,7 @@ function products_report(solution; marginal_costs = true)::DataFrame
|
|||||||
year,
|
year,
|
||||||
amount,
|
amount,
|
||||||
marginal_cost,
|
marginal_cost,
|
||||||
|
amount_disposed,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -169,6 +169,12 @@
|
|||||||
},
|
},
|
||||||
"initial amounts": {
|
"initial amounts": {
|
||||||
"$ref": "#/definitions/InitialAmount"
|
"$ref": "#/definitions/InitialAmount"
|
||||||
|
},
|
||||||
|
"disposal limit (tonne)": {
|
||||||
|
"$ref": "#/definitions/TimeSeries"
|
||||||
|
},
|
||||||
|
"disposal cost ($/tonne)": {
|
||||||
|
"$ref": "#/definitions/TimeSeries"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
@@ -40,7 +40,14 @@ using RELOG
|
|||||||
@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]
|
||||||
|
|
||||||
|
p1 = product_name_to_product["P1"]
|
||||||
|
@test p1.disposal_limit == [1.0, 1.0]
|
||||||
|
@test p1.disposal_cost == [-1000.0, -1000.0]
|
||||||
|
|
||||||
p2 = product_name_to_product["P2"]
|
p2 = product_name_to_product["P2"]
|
||||||
|
@test p2.disposal_limit == [0.0, 0.0]
|
||||||
|
@test p2.disposal_cost == [0.0, 0.0]
|
||||||
|
|
||||||
p3 = product_name_to_product["P3"]
|
p3 = product_name_to_product["P3"]
|
||||||
@test length(plant.output) == 2
|
@test length(plant.output) == 2
|
||||||
@test plant.output[p2] == 0.2
|
@test plant.output[p2] == 0.2
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
)
|
)
|
||||||
|
|
||||||
@test length(model[:flow]) == 76
|
@test length(model[:flow]) == 76
|
||||||
@test length(model[:dispose]) == 16
|
@test length(model[:plant_dispose]) == 16
|
||||||
@test length(model[:open_plant]) == 12
|
@test length(model[:open_plant]) == 12
|
||||||
@test length(model[:capacity]) == 12
|
@test length(model[:capacity]) == 12
|
||||||
@test length(model[:expansion]) == 12
|
@test length(model[:expansion]) == 12
|
||||||
@@ -32,7 +32,7 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats
|
|||||||
@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[:dispose][shipping_node_by_loc_and_prod_names["L1", "P2"], 1]
|
v = model[:plant_dispose][shipping_node_by_loc_and_prod_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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ basedir = dirname(@__FILE__)
|
|||||||
@test "F2" in keys(solution["Plants"])
|
@test "F2" in keys(solution["Plants"])
|
||||||
@test "F3" in keys(solution["Plants"])
|
@test "F3" in keys(solution["Plants"])
|
||||||
@test "F4" in keys(solution["Plants"])
|
@test "F4" in keys(solution["Plants"])
|
||||||
|
|
||||||
|
@test "Products" in keys(solution)
|
||||||
|
@test "P1" in keys(solution["Products"])
|
||||||
|
@test "C1" in keys(solution["Products"]["P1"])
|
||||||
|
@test "Dispose (tonne)" in keys(solution["Products"]["P1"]["C1"])
|
||||||
|
|
||||||
|
total_disposal =
|
||||||
|
sum([loc["Dispose (tonne)"] for loc in values(solution["Products"]["P1"])])
|
||||||
|
@test total_disposal == [1.0, 1.0]
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "solve (heuristic)" begin
|
@testset "solve (heuristic)" begin
|
||||||
|
|||||||
Reference in New Issue
Block a user