mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-06 07:48:50 -06:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
de27a6202d
|
|||
|
7d4a763910
|
|||
|
8432c49050
|
|||
|
2d860326fe
|
|||
|
be37934b87
|
|||
|
3c354ec3e4
|
|||
|
f5a92358d7
|
|||
|
69f205be77
|
|||
|
3b3ecbde27
|
28
.zenodo.json
Normal file
28
.zenodo.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"creators": [
|
||||||
|
{
|
||||||
|
"orcid": "0000-0002-5022-9802",
|
||||||
|
"affiliation": "Argonne National Laboratory",
|
||||||
|
"name": "Santos Xavier, Alinson"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"orcid": "0000-0002-3426-9425",
|
||||||
|
"affiliation": "Argonne National Laboratory",
|
||||||
|
"name": "Iloeje, Chukwunwike"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"affiliation": "Argonne National Laboratory",
|
||||||
|
"name": "Atkins, John"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"affiliation": "Argonne National Laboratory",
|
||||||
|
"name": "Sun, Kyle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"affiliation": "Argonne National Laboratory",
|
||||||
|
"name": "Gallier, Audrey"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "RELOG: Reverse Logistics Optimization",
|
||||||
|
"description": "<b>RELOG</b> is a supply chain optimization package focusing on reverse logistics and reverse manufacturing. For example, the package can be used to determine where to build recycling plants, what sizes should they have and which customers should be served by which plants. The package supports customized reverse logistics pipelines, with multiple types of plants, multiple types of product and multiple time periods."
|
||||||
|
}
|
||||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -11,6 +11,29 @@ All notable changes to this project will be documented in this file.
|
|||||||
[semver]: https://semver.org/spec/v2.0.0.html
|
[semver]: https://semver.org/spec/v2.0.0.html
|
||||||
[pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
|
[pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
|
||||||
|
|
||||||
|
## [0.7.2] -- 2023-03-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Core: Fixed modeling issue with collection disposal
|
||||||
|
- Core: Fix column names in products CSV file
|
||||||
|
|
||||||
|
## [0.7.1] -- 2023-03-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Core: Add `write_reports` function
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Web UI: Disable usage of heuristic method
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Core: Prevent plants from sending products to themselves
|
||||||
|
- Core: Enforce constraint that, if plant is closed, storage cannot be used
|
||||||
|
- Web UI: Fix parsing bug in disposal limit
|
||||||
|
|
||||||
## [0.7.0] -- 2023-02-23
|
## [0.7.0] -- 2023-02-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name = "RELOG"
|
name = "RELOG"
|
||||||
uuid = "a2afcdf7-cf04-4913-85f9-c0d81ddf2008"
|
uuid = "a2afcdf7-cf04-4913-85f9-c0d81ddf2008"
|
||||||
authors = ["Alinson S Xavier <axavier@anl.gov>"]
|
authors = ["Alinson S Xavier <axavier@anl.gov>"]
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
CRC = "44b605c4-b955-5f2b-9b6d-d2bd01d3d205"
|
CRC = "44b605c4-b955-5f2b-9b6d-d2bd01d3d205"
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ const InputPage = () => {
|
|||||||
"disposal limit (tonne)",
|
"disposal limit (tonne)",
|
||||||
].forEach((key) => {
|
].forEach((key) => {
|
||||||
newData.plants[plantName][key] = { ...newData.plants[plantName][key] };
|
newData.plants[plantName][key] = { ...newData.plants[plantName][key] };
|
||||||
newData.plants[plantName][key][productName] = 0;
|
newData.plants[plantName][key][productName] = "0";
|
||||||
});
|
});
|
||||||
save(newData);
|
save(newData);
|
||||||
return newData;
|
return newData;
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ export const exportPlant = (original, parameters) => {
|
|||||||
const v = exportValueAcf(dispCost, origDict);
|
const v = exportValueAcf(dispCost, origDict);
|
||||||
if (v) {
|
if (v) {
|
||||||
resDict.disposal[dispName] = { "cost ($/tonne)": v };
|
resDict.disposal[dispName] = { "cost ($/tonne)": v };
|
||||||
const limit = original["disposal limit (tonne)"][dispName];
|
const limit = String(original["disposal limit (tonne)"][dispName]);
|
||||||
if (limit.length > 0) {
|
if (limit.length > 0) {
|
||||||
resDict.disposal[dispName]["limit (tonne)"] = exportValue(
|
resDict.disposal[dispName]["limit (tonne)"] = exportValue(
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ const samplePlantsOriginal = [
|
|||||||
},
|
},
|
||||||
"disposal limit (tonne)": {
|
"disposal limit (tonne)": {
|
||||||
"Hydrogen gas": "10",
|
"Hydrogen gas": "10",
|
||||||
"Carbon dioxide": "",
|
"Carbon dioxide": 0,
|
||||||
Tar: "",
|
Tar: "",
|
||||||
},
|
},
|
||||||
"emissions (tonne/tonne)": {
|
"emissions (tonne/tonne)": {
|
||||||
@@ -406,6 +406,7 @@ const samplePlantsExported = [
|
|||||||
},
|
},
|
||||||
"Carbon dioxide": {
|
"Carbon dioxide": {
|
||||||
"cost ($/tonne)": [0, 0, 0],
|
"cost ($/tonne)": [0, 0, 0],
|
||||||
|
"limit (tonne)": [0, 0, 0],
|
||||||
},
|
},
|
||||||
Tar: {
|
Tar: {
|
||||||
"cost ($/tonne)": [200, 400, 800],
|
"cost ($/tonne)": [200, 400, 800],
|
||||||
@@ -439,6 +440,7 @@ const samplePlantsExported = [
|
|||||||
},
|
},
|
||||||
"Carbon dioxide": {
|
"Carbon dioxide": {
|
||||||
"cost ($/tonne)": [0, 0, 0],
|
"cost ($/tonne)": [0, 0, 0],
|
||||||
|
"limit (tonne)": [0, 0, 0],
|
||||||
},
|
},
|
||||||
Tar: {
|
Tar: {
|
||||||
"cost ($/tonne)": [100, 200.0, 400],
|
"cost ($/tonne)": [100, 200.0, 400],
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ function build_graph(instance::Instance)::Graph
|
|||||||
# 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]
|
||||||
|
source.location != dest.location || continue
|
||||||
distance = _calculate_distance(
|
distance = _calculate_distance(
|
||||||
source.location.latitude,
|
source.location.latitude,
|
||||||
source.location.longitude,
|
source.location.longitude,
|
||||||
|
|||||||
@@ -184,8 +184,8 @@ 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) ==
|
sum(model[:flow][a, t] for a in n.outgoing_arcs) +
|
||||||
n.location.amount[t] + model[:collection_dispose][n, t]
|
model[:collection_dispose][n, t] == n.location.amount[t]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
for prod in model[:instance].products
|
for prod in model[:instance].products
|
||||||
@@ -237,6 +237,12 @@ function create_process_node_constraints!(model::JuMP.Model)
|
|||||||
model[:capacity][n, t] <= n.location.sizes[2].capacity * model[:is_open][n, t]
|
model[:capacity][n, t] <= n.location.sizes[2].capacity * model[:is_open][n, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# If plant is closed, storage cannot be used
|
||||||
|
@constraint(
|
||||||
|
model,
|
||||||
|
model[:store][n, t] <= n.location.storage_limit * model[:is_open][n, t]
|
||||||
|
)
|
||||||
|
|
||||||
# If plant is open, capacity is greater than base
|
# If plant is open, capacity is greater than base
|
||||||
@constraint(
|
@constraint(
|
||||||
model,
|
model,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function plant_emissions_report(solution)::DataFrame
|
|||||||
location_name,
|
location_name,
|
||||||
year,
|
year,
|
||||||
emission_name,
|
emission_name,
|
||||||
round(emission_amount[year], digits = 2),
|
round(emission_amount[year], digits = 6),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function plant_outputs_report(solution)::DataFrame
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sent = round.(sent, digits = 2)
|
sent = round.(sent, digits = 6)
|
||||||
|
|
||||||
disposal_amount = zeros(T)
|
disposal_amount = zeros(T)
|
||||||
disposal_cost = zeros(T)
|
disposal_cost = zeros(T)
|
||||||
@@ -38,8 +38,8 @@ function plant_outputs_report(solution)::DataFrame
|
|||||||
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 = 6)
|
||||||
disposal_cost = round.(disposal_cost, digits = 2)
|
disposal_cost = round.(disposal_cost, digits = 6)
|
||||||
|
|
||||||
for year = 1:T
|
for year = 1:T
|
||||||
push!(
|
push!(
|
||||||
@@ -49,7 +49,7 @@ function plant_outputs_report(solution)::DataFrame
|
|||||||
location_name,
|
location_name,
|
||||||
year,
|
year,
|
||||||
product_name,
|
product_name,
|
||||||
round(amount_produced[year], digits = 2),
|
round(amount_produced[year], digits = 6),
|
||||||
sent[year],
|
sent[year],
|
||||||
disposal_amount[year],
|
disposal_amount[year],
|
||||||
disposal_cost[year],
|
disposal_cost[year],
|
||||||
|
|||||||
@@ -28,25 +28,25 @@ function plants_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 year = 1:T
|
for year = 1:T
|
||||||
capacity = round(location_dict["Capacity (tonne)"][year], digits = 2)
|
capacity = round(location_dict["Capacity (tonne)"][year], digits = 6)
|
||||||
received = round(location_dict["Total input (tonne)"][year], digits = 2)
|
received = round(location_dict["Total input (tonne)"][year], digits = 6)
|
||||||
processed = round(location_dict["Process (tonne)"][year], digits = 2)
|
processed = round(location_dict["Process (tonne)"][year], digits = 6)
|
||||||
in_storage = round(location_dict["Storage (tonne)"][year], digits = 2)
|
in_storage = round(location_dict["Storage (tonne)"][year], digits = 6)
|
||||||
utilization_factor = round(processed / capacity * 100.0, digits = 2)
|
utilization_factor = round(processed / capacity * 100.0, digits = 6)
|
||||||
energy = round(location_dict["Energy (GJ)"][year], digits = 2)
|
energy = round(location_dict["Energy (GJ)"][year], digits = 6)
|
||||||
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 = 6)
|
||||||
expansion_cost =
|
expansion_cost =
|
||||||
round(location_dict["Expansion cost (\$)"][year], digits = 2)
|
round(location_dict["Expansion cost (\$)"][year], digits = 6)
|
||||||
fixed_cost =
|
fixed_cost =
|
||||||
round(location_dict["Fixed operating cost (\$)"][year], digits = 2)
|
round(location_dict["Fixed operating cost (\$)"][year], digits = 6)
|
||||||
var_cost =
|
var_cost =
|
||||||
round(location_dict["Variable operating cost (\$)"][year], digits = 2)
|
round(location_dict["Variable operating cost (\$)"][year], digits = 6)
|
||||||
storage_cost = round(location_dict["Storage cost (\$)"][year], digits = 2)
|
storage_cost = round(location_dict["Storage cost (\$)"][year], digits = 6)
|
||||||
total_cost = round(
|
total_cost = round(
|
||||||
opening_cost + expansion_cost + fixed_cost + var_cost + storage_cost,
|
opening_cost + expansion_cost + fixed_cost + var_cost + storage_cost,
|
||||||
digits = 2,
|
digits = 6,
|
||||||
)
|
)
|
||||||
push!(
|
push!(
|
||||||
df,
|
df,
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ function products_report(solution; marginal_costs = true)::DataFrame
|
|||||||
longitude,
|
longitude,
|
||||||
year,
|
year,
|
||||||
amount,
|
amount,
|
||||||
marginal_cost,
|
|
||||||
amount_disposed,
|
amount_disposed,
|
||||||
|
marginal_cost,
|
||||||
acquisition_cost,
|
acquisition_cost,
|
||||||
disposal_cost,
|
disposal_cost,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -42,24 +42,24 @@ function transportation_report(solution)::DataFrame
|
|||||||
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||||
dst_location_dict["Input product"],
|
dst_location_dict["Input product"],
|
||||||
year,
|
year,
|
||||||
round(src_location_dict["Distance (km)"], digits = 2),
|
round(src_location_dict["Distance (km)"], digits = 6),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Amount (tonne)"][year],
|
src_location_dict["Amount (tonne)"][year],
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Amount (tonne)"][year] *
|
src_location_dict["Amount (tonne)"][year] *
|
||||||
src_location_dict["Distance (km)"],
|
src_location_dict["Distance (km)"],
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Transportation cost (\$)"][year],
|
src_location_dict["Transportation cost (\$)"][year],
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Transportation energy (J)"][year] /
|
src_location_dict["Transportation energy (J)"][year] /
|
||||||
1e9,
|
1e9,
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,18 +44,18 @@ function transportation_emissions_report(solution)::DataFrame
|
|||||||
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||||
dst_location_dict["Input product"],
|
dst_location_dict["Input product"],
|
||||||
year,
|
year,
|
||||||
round(src_location_dict["Distance (km)"], digits = 2),
|
round(src_location_dict["Distance (km)"], digits = 6),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Amount (tonne)"][year],
|
src_location_dict["Amount (tonne)"][year],
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
round(
|
round(
|
||||||
src_location_dict["Amount (tonne)"][year] *
|
src_location_dict["Amount (tonne)"][year] *
|
||||||
src_location_dict["Distance (km)"],
|
src_location_dict["Distance (km)"],
|
||||||
digits = 2,
|
digits = 6,
|
||||||
),
|
),
|
||||||
emission_name,
|
emission_name,
|
||||||
round(emission_amount[year], digits = 2),
|
round(emission_amount[year], digits = 6),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,3 +12,13 @@ function write(solution::AbstractDict, filename::AbstractString)
|
|||||||
JSON.print(file, solution, 2)
|
JSON.print(file, solution, 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function write_reports(solution::AbstractDict, basename::AbstractString)
|
||||||
|
RELOG.write_products_report(solution, "$(basename)_products.csv")
|
||||||
|
RELOG.write_plants_report(solution, "$(basename)_plants.csv")
|
||||||
|
RELOG.write_plant_outputs_report(solution, "$(basename)_plant_outputs.csv")
|
||||||
|
RELOG.write_plant_emissions_report(solution, "$(basename)_plant_emissions.csv")
|
||||||
|
RELOG.write_transportation_report(solution, "$(basename)_tr.csv")
|
||||||
|
RELOG.write_transportation_emissions_report(solution, "$(basename)_tr_emissions.csv")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function solve(root, filename)
|
|||||||
)
|
)
|
||||||
ref_solution, ref_model = RELOG.solve(
|
ref_solution, ref_model = RELOG.solve(
|
||||||
ref_file,
|
ref_file,
|
||||||
heuristic = true,
|
|
||||||
optimizer = optimizer,
|
optimizer = optimizer,
|
||||||
lp_optimizer = HiGHS.Optimizer,
|
lp_optimizer = HiGHS.Optimizer,
|
||||||
return_model = true,
|
return_model = true,
|
||||||
|
|||||||
Reference in New Issue
Block a user