mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-05 23:38:52 -06:00
Let model decide plant size (linear model)
This commit is contained in:
@@ -15,6 +15,12 @@ git-tree-sha1 = "5b08ed6036d9d3f0ee6369410b830f8873d4024c"
|
||||
uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
|
||||
version = "0.5.8"
|
||||
|
||||
[[Bzip2_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "3663bfffede2ef41358b6fc2e1d8a6d50b3c3904"
|
||||
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
|
||||
version = "1.0.6+2"
|
||||
|
||||
[[Calculus]]
|
||||
deps = ["LinearAlgebra"]
|
||||
git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad"
|
||||
@@ -34,16 +40,16 @@ uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
|
||||
version = "0.5.8"
|
||||
|
||||
[[CodecBzip2]]
|
||||
deps = ["BinaryProvider", "Libdl", "TranscodingStreams"]
|
||||
git-tree-sha1 = "5db086e510c11b4c87d05067627eadb2dc079995"
|
||||
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
|
||||
git-tree-sha1 = "2fee975d68f9a8b22187ae86e33c0829b30cf231"
|
||||
uuid = "523fee87-0ab8-5b00-afb7-3ecf72e48cfd"
|
||||
version = "0.6.0"
|
||||
version = "0.7.1"
|
||||
|
||||
[[CodecZlib]]
|
||||
deps = ["BinaryProvider", "Libdl", "TranscodingStreams"]
|
||||
git-tree-sha1 = "05916673a2627dd91b4969ff8ba6941bc85a960e"
|
||||
deps = ["TranscodingStreams", "Zlib_jll"]
|
||||
git-tree-sha1 = "ded953804d019afa9a3f98981d99b33e3db7b6da"
|
||||
uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
|
||||
[[CommonSubexpressions]]
|
||||
deps = ["Test"]
|
||||
@@ -53,9 +59,9 @@ version = "0.2.0"
|
||||
|
||||
[[CompilerSupportLibraries_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "b57c5d019367c90f234a7bc7e24ff0a84971da5d"
|
||||
git-tree-sha1 = "7c4f882c41faa72118841185afc58a2eb00ef612"
|
||||
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
|
||||
version = "0.2.0+1"
|
||||
version = "0.3.3+0"
|
||||
|
||||
[[CoordinateTransformations]]
|
||||
deps = ["LinearAlgebra", "Rotations", "StaticArrays"]
|
||||
@@ -65,9 +71,9 @@ version = "0.5.1"
|
||||
|
||||
[[DataStructures]]
|
||||
deps = ["InteractiveUtils", "OrderedCollections"]
|
||||
git-tree-sha1 = "5a431d46abf2ef2a4d5d00bd0ae61f651cf854c8"
|
||||
git-tree-sha1 = "73eb18320fe3ba58790c8b8f6f89420f0a622773"
|
||||
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
version = "0.17.10"
|
||||
version = "0.17.11"
|
||||
|
||||
[[Dates]]
|
||||
deps = ["Printf"]
|
||||
@@ -94,9 +100,9 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
|
||||
|
||||
[[ForwardDiff]]
|
||||
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "NaNMath", "Random", "SpecialFunctions", "StaticArrays"]
|
||||
git-tree-sha1 = "88b082d492be6b63f967b6c96b352e25ced1a34c"
|
||||
git-tree-sha1 = "869540e4367122fbffaace383a5bdc34d6e5e5ac"
|
||||
uuid = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
version = "0.10.9"
|
||||
version = "0.10.10"
|
||||
|
||||
[[Geodesy]]
|
||||
deps = ["CoordinateTransformations", "Dates", "LinearAlgebra", "StaticArrays", "Test"]
|
||||
@@ -106,9 +112,9 @@ version = "0.5.0"
|
||||
|
||||
[[HTTP]]
|
||||
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"]
|
||||
git-tree-sha1 = "8d9bdd55c9d0d6ddf08f8b5229f90b7f274b6777"
|
||||
git-tree-sha1 = "cd60d9a575d3b70c026d7e714212fd4ecf86b4bb"
|
||||
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
version = "0.8.12"
|
||||
version = "0.8.13"
|
||||
|
||||
[[IniFile]]
|
||||
deps = ["Test"]
|
||||
@@ -169,9 +175,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||
|
||||
[[MathOptInterface]]
|
||||
deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "JSONSchema", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "SparseArrays", "Test", "Unicode"]
|
||||
git-tree-sha1 = "f0d60e42d8b64dd1b511e2dc13e0b72ba1dfc9cf"
|
||||
git-tree-sha1 = "27f2ef85879b8f1d144266ab44f076ba0dfbd8a1"
|
||||
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||
version = "0.9.12"
|
||||
version = "0.9.13"
|
||||
|
||||
[[MathProgBase]]
|
||||
deps = ["LinearAlgebra", "SparseArrays"]
|
||||
@@ -196,9 +202,9 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||
|
||||
[[MutableArithmetics]]
|
||||
deps = ["LinearAlgebra", "SparseArrays", "Test"]
|
||||
git-tree-sha1 = "020d4f22e1151e0613edf91a56535379564c1ce8"
|
||||
git-tree-sha1 = "e1edd618a8f39d16f8595dd622a63b25f759cf8a"
|
||||
uuid = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
|
||||
version = "0.2.7"
|
||||
version = "0.2.9"
|
||||
|
||||
[[NaNMath]]
|
||||
git-tree-sha1 = "928b8ca9b2791081dc71a51c55347c27c618760f"
|
||||
@@ -219,9 +225,9 @@ version = "1.1.0"
|
||||
|
||||
[[Parsers]]
|
||||
deps = ["Dates", "Test"]
|
||||
git-tree-sha1 = "0c16b3179190d3046c073440d94172cfc3bb0553"
|
||||
git-tree-sha1 = "75d07cb840c300084634b4991761886d0d762724"
|
||||
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
||||
version = "0.3.12"
|
||||
version = "1.0.1"
|
||||
|
||||
[[Pkg]]
|
||||
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Test", "UUIDs"]
|
||||
@@ -302,3 +308,9 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
|
||||
[[Unicode]]
|
||||
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
|
||||
|
||||
[[Zlib_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "2f6c3e15e20e036ee0a0965879b31442b7ec50fa"
|
||||
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
|
||||
version = "1.2.11+9"
|
||||
|
||||
11
README.md
11
README.md
@@ -38,7 +38,7 @@ The first step when using ReverseManufacturing.jl is describing the reverse manu
|
||||
|
||||
The **products** section describes all products and subproducts in the simulation. The field `instance["products"]` is a dictionary mapping the name of the product to a dictionary which describes its characteristics. Each product description contains the following keys:
|
||||
|
||||
* `transportation cost`, the cost (in dollars per km) to transport this product.
|
||||
* `transportation cost`, the cost (in dollars per km per kg) to transport this product.
|
||||
* `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.
|
||||
|
||||
Each product may have some amount available at the beginning of the simulation. In this case, the key `initial amounts` maps to a dictionary with the following keys:
|
||||
@@ -62,6 +62,15 @@ Each type of plant is associated with a set of potential locations where it can
|
||||
* `opening cost`, the cost (in dollars) to open the plant.
|
||||
* `fixed operating cost`, the cost (in dollars) to keep the plant open, even if the plant doesn't process anything.
|
||||
* `variable operating cost`, the cost (in dollars per kg) that the plant incurs to process each kg of input.
|
||||
* `base capacity`, the amount of input (in kg) the plant can process when zero dollars are spent on expansion. If unlimited, this key may be omitted.
|
||||
* `max capacity`, the amount (in kg) the plant can process when the maximum amount of dollars are spent on expansion. If unlimited, this key may be omitted.
|
||||
* `expansion cost`, the cost (in dollars per kg) to increase the plant capacity beyond its base capacity. If zero, this key may be omitted.
|
||||
* `disposal`, a dictionary describing what products can be disposed locally at the plant.
|
||||
|
||||
The keys in the disposal dictionary should be the names of the products. The values are dictionaries with the following keys:
|
||||
|
||||
* `cost`, the cost (in dollars per kg) to dispose of the product.
|
||||
* `limit`, the maximum amount (in kg) that can be disposed of. If an unlimited amount can be disposed, this key may be omitted.
|
||||
|
||||
### Optimizing
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"products": {
|
||||
"P1": {
|
||||
"transportation cost": 1.50,
|
||||
"transportation cost": 0.015,
|
||||
"initial amounts": {
|
||||
"C1": {
|
||||
"latitude": 7.0,
|
||||
@@ -56,13 +56,13 @@
|
||||
}
|
||||
},
|
||||
"P2": {
|
||||
"transportation cost": 2.00
|
||||
"transportation cost": 0.02
|
||||
},
|
||||
"P3": {
|
||||
"transportation cost": 1.25
|
||||
"transportation cost": 0.0125
|
||||
},
|
||||
"P4": {
|
||||
"transportation cost": 1.75
|
||||
"transportation cost": 0.0175
|
||||
}
|
||||
},
|
||||
"plants": {
|
||||
@@ -76,10 +76,12 @@
|
||||
"L1": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"capacity": 5000,
|
||||
"opening cost": 2000,
|
||||
"fixed operating cost": 70.0,
|
||||
"variable operating cost": 70.0,
|
||||
"opening cost": 500,
|
||||
"base capacity": 250.0,
|
||||
"max capacity": 1000.0,
|
||||
"expansion cost": 1.0,
|
||||
"fixed operating cost": 30.0,
|
||||
"variable operating cost": 30.0,
|
||||
"disposal": {
|
||||
"P2": {
|
||||
"cost": -10.0,
|
||||
@@ -95,8 +97,9 @@
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.5,
|
||||
"opening cost": 1000,
|
||||
"capacity": 7500,
|
||||
"opening cost": 1000,
|
||||
"base capacity": 0.0,
|
||||
"max capacity": 10000.0,
|
||||
"expansion cost": 1.0,
|
||||
"fixed operating cost": 50.0,
|
||||
"variable operating cost": 50.0
|
||||
}
|
||||
|
||||
51
src/model.jl
51
src/model.jl
@@ -21,7 +21,9 @@ mutable struct ProcessNode <: Node
|
||||
incoming_arcs::Array
|
||||
outgoing_arcs::Array
|
||||
fixed_cost::Float64
|
||||
capacity::Float64
|
||||
expansion_cost::Float64
|
||||
base_capacity::Float64
|
||||
max_capacity::Float64
|
||||
end
|
||||
|
||||
mutable struct ShippingNode <: Node
|
||||
@@ -83,6 +85,10 @@ function build_model(instance::ReverseManufacturingInstance,
|
||||
upper_bound = n.disposal_limit)
|
||||
for n in values(shipping_nodes))
|
||||
vars.open_plant = Dict(n => @variable(mip, binary=true) for n in values(process_nodes))
|
||||
vars.capacity = Dict(n => @variable(mip, lower_bound = 0, upper_bound = n.max_capacity)
|
||||
for n in values(process_nodes))
|
||||
vars.expansion = Dict(n => @variable(mip, lower_bound = 0, upper_bound = (n.max_capacity - n.base_capacity))
|
||||
for n in values(process_nodes))
|
||||
create_shipping_node_constraints!(mip, shipping_nodes, vars)
|
||||
create_process_node_constraints!(mip, process_nodes, vars)
|
||||
|
||||
@@ -101,6 +107,11 @@ function build_model(instance::ReverseManufacturingInstance,
|
||||
add_to_expression!(obj, n.fixed_cost, vars.open_plant[n])
|
||||
end
|
||||
|
||||
# Expansion cost
|
||||
for n in tqdm(values(process_nodes))
|
||||
add_to_expression!(obj, n.expansion_cost, vars.expansion[n])
|
||||
end
|
||||
|
||||
# Disposal costs
|
||||
for n in tqdm(values(shipping_nodes))
|
||||
add_to_expression!(obj, n.disposal_cost, vars.dispose[n])
|
||||
@@ -132,8 +143,15 @@ function create_process_node_constraints!(mip, nodes, vars)
|
||||
for a in n.outgoing_arcs
|
||||
@constraint(mip, vars.flow[a] == a.values["weight"] * input_sum)
|
||||
end
|
||||
# If plant is closed, input must be zero. If plant is opened, input must be below capacity.
|
||||
@constraint(mip, input_sum <= n.capacity * vars.open_plant[n])
|
||||
|
||||
# If plant is closed, capacity is zero.
|
||||
@constraint(mip, vars.capacity[n] <= n.max_capacity * vars.open_plant[n])
|
||||
|
||||
# Capacity is linked to expansion
|
||||
@constraint(mip, vars.capacity[n] <= n.base_capacity + vars.expansion[n])
|
||||
|
||||
# Input sum must be smaller than capacity
|
||||
@constraint(mip, input_sum <= vars.capacity[n])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -166,19 +184,28 @@ function create_nodes_and_arcs(instance)
|
||||
# Process nodes for each plant
|
||||
for plant in product["input plants"]
|
||||
for (location_name, location) in plant["locations"]
|
||||
cost = location["opening cost"] + location["fixed operating cost"]
|
||||
if "capacity" in keys(location)
|
||||
capacity = location["capacity"]
|
||||
else
|
||||
capacity = 1e10
|
||||
base_capacity = 1e8
|
||||
max_capacity = 1e8
|
||||
expansion_cost = 0.0
|
||||
fixed_cost = location["opening cost"] + location["fixed operating cost"]
|
||||
if "base capacity" in keys(location)
|
||||
base_capacity = location["base capacity"]
|
||||
end
|
||||
if "max capacity" in keys(location)
|
||||
max_capacity = location["max capacity"]
|
||||
end
|
||||
if "expansion cost" in keys(location)
|
||||
expansion_cost = location["expansion cost"]
|
||||
end
|
||||
n = ProcessNode(product_name,
|
||||
plant["name"],
|
||||
location_name,
|
||||
[], # incoming_arcs
|
||||
[], # outgoing_arcs
|
||||
cost,
|
||||
capacity)
|
||||
fixed_cost,
|
||||
expansion_cost,
|
||||
base_capacity,
|
||||
max_capacity)
|
||||
process_nodes[n.product_name, n.plant_name, n.location_name] = n
|
||||
end
|
||||
end
|
||||
@@ -320,6 +347,7 @@ function get_solution(instance::ReverseManufacturingInstance,
|
||||
"transportation" => 0.0,
|
||||
"disposal" => 0.0,
|
||||
"total" => 0.0,
|
||||
"expansion" => 0.0,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -342,10 +370,13 @@ function get_solution(instance::ReverseManufacturingInstance,
|
||||
"total output" => Dict(),
|
||||
"latitude" => location["latitude"],
|
||||
"longitude" => location["longitude"],
|
||||
"capacity" => round(JuMP.value(model.vars.capacity[process_node]), digits=2)
|
||||
)
|
||||
|
||||
plant_loc_dict["fixed cost"] = round(vals[process_node] * process_node.fixed_cost, digits=5)
|
||||
plant_loc_dict["expansion cost"] = round(JuMP.value(model.vars.expansion[process_node]) * process_node.expansion_cost, digits=5)
|
||||
output["costs"]["fixed"] += plant_loc_dict["fixed cost"]
|
||||
output["costs"]["expansion"] += plant_loc_dict["expansion cost"]
|
||||
|
||||
# Inputs
|
||||
for a in process_node.incoming_arcs
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
"variable operating cost": { "type": "number" },
|
||||
"fixed operating cost": { "type": "number" },
|
||||
"opening cost": { "type": "number" },
|
||||
"capacity": { "type": "number" },
|
||||
"base capacity": { "type": "number" },
|
||||
"max capacity": { "type": "number" },
|
||||
"expansion cost": { "type": "number" },
|
||||
"disposal": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
||||
@@ -13,6 +13,6 @@ using ReverseManufacturing
|
||||
|
||||
@test sort(collect(keys(products))) == ["P1", "P2", "P3", "P4"]
|
||||
@test products["P1"]["input plants"] == [plants["F1"]]
|
||||
@test products["P1"]["transportation cost"] == 1.5
|
||||
@test products["P1"]["transportation cost"] == 0.015
|
||||
@test products["P1"]["initial amounts"]["C1"]["latitude"] == 7.0
|
||||
end
|
||||
|
||||
@@ -35,8 +35,8 @@ using ReverseManufacturing, Cbc, JuMP, Printf, JSON
|
||||
arc = p1_orig_c1.outgoing_arcs[1]
|
||||
@test arc.dest.location_name == "L1"
|
||||
@test arc.values["distance"] == 1095.62
|
||||
@test round(arc.costs["transportation"], digits=2) == 1643.43
|
||||
@test arc.costs["variable"] == 70.0
|
||||
@test round(arc.costs["transportation"], digits=2) == 16.43
|
||||
@test arc.costs["variable"] == 30.0
|
||||
|
||||
p2_f1_l1 = model.shipping_nodes["P2", "F1", "L1"]
|
||||
p2_f2_l3 = model.process_nodes["P2", "F2", "L3"]
|
||||
@@ -59,4 +59,6 @@ end
|
||||
@test "F4" in keys(solution["plants"])
|
||||
@test "L2" in keys(solution["plants"]["F1"])
|
||||
@test "total output" in keys(solution["plants"]["F1"]["L2"])
|
||||
|
||||
@test "capacity" in keys(solution["plants"]["F1"]["L1"])
|
||||
end
|
||||
Reference in New Issue
Block a user