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
|
||||
[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
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name = "RELOG"
|
||||
uuid = "a2afcdf7-cf04-4913-85f9-c0d81ddf2008"
|
||||
authors = ["Alinson S Xavier <axavier@anl.gov>"]
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
|
||||
[deps]
|
||||
CRC = "44b605c4-b955-5f2b-9b6d-d2bd01d3d205"
|
||||
|
||||
@@ -235,7 +235,7 @@ const InputPage = () => {
|
||||
"disposal limit (tonne)",
|
||||
].forEach((key) => {
|
||||
newData.plants[plantName][key] = { ...newData.plants[plantName][key] };
|
||||
newData.plants[plantName][key][productName] = 0;
|
||||
newData.plants[plantName][key][productName] = "0";
|
||||
});
|
||||
save(newData);
|
||||
return newData;
|
||||
|
||||
@@ -277,7 +277,7 @@ export const exportPlant = (original, parameters) => {
|
||||
const v = exportValueAcf(dispCost, origDict);
|
||||
if (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) {
|
||||
resDict.disposal[dispName]["limit (tonne)"] = exportValue(
|
||||
limit,
|
||||
|
||||
@@ -213,7 +213,7 @@ const samplePlantsOriginal = [
|
||||
},
|
||||
"disposal limit (tonne)": {
|
||||
"Hydrogen gas": "10",
|
||||
"Carbon dioxide": "",
|
||||
"Carbon dioxide": 0,
|
||||
Tar: "",
|
||||
},
|
||||
"emissions (tonne/tonne)": {
|
||||
@@ -406,6 +406,7 @@ const samplePlantsExported = [
|
||||
},
|
||||
"Carbon dioxide": {
|
||||
"cost ($/tonne)": [0, 0, 0],
|
||||
"limit (tonne)": [0, 0, 0],
|
||||
},
|
||||
Tar: {
|
||||
"cost ($/tonne)": [200, 400, 800],
|
||||
@@ -439,6 +440,7 @@ const samplePlantsExported = [
|
||||
},
|
||||
"Carbon dioxide": {
|
||||
"cost ($/tonne)": [0, 0, 0],
|
||||
"limit (tonne)": [0, 0, 0],
|
||||
},
|
||||
Tar: {
|
||||
"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
|
||||
for source in [collection_shipping_nodes; plant_shipping_nodes]
|
||||
for dest in process_nodes_by_input_product[source.product]
|
||||
source.location != dest.location || continue
|
||||
distance = _calculate_distance(
|
||||
source.location.latitude,
|
||||
source.location.longitude,
|
||||
|
||||
@@ -184,8 +184,8 @@ function create_shipping_node_constraints!(model::JuMP.Model)
|
||||
for n in graph.collection_shipping_nodes
|
||||
model[:eq_balance][n, t] = @constraint(
|
||||
model,
|
||||
sum(model[:flow][a, t] for a in n.outgoing_arcs) ==
|
||||
n.location.amount[t] + model[:collection_dispose][n, t]
|
||||
sum(model[:flow][a, t] for a in n.outgoing_arcs) +
|
||||
model[:collection_dispose][n, t] == n.location.amount[t]
|
||||
)
|
||||
end
|
||||
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]
|
||||
)
|
||||
|
||||
# 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
|
||||
@constraint(
|
||||
model,
|
||||
|
||||
@@ -24,7 +24,7 @@ function plant_emissions_report(solution)::DataFrame
|
||||
location_name,
|
||||
year,
|
||||
emission_name,
|
||||
round(emission_amount[year], digits = 2),
|
||||
round(emission_amount[year], digits = 6),
|
||||
],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ function plant_outputs_report(solution)::DataFrame
|
||||
end
|
||||
end
|
||||
end
|
||||
sent = round.(sent, digits = 2)
|
||||
sent = round.(sent, digits = 6)
|
||||
|
||||
disposal_amount = 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_cost += disposal_dict[product_name]["Cost (\$)"]
|
||||
end
|
||||
disposal_amount = round.(disposal_amount, digits = 2)
|
||||
disposal_cost = round.(disposal_cost, digits = 2)
|
||||
disposal_amount = round.(disposal_amount, digits = 6)
|
||||
disposal_cost = round.(disposal_cost, digits = 6)
|
||||
|
||||
for year = 1:T
|
||||
push!(
|
||||
@@ -49,7 +49,7 @@ function plant_outputs_report(solution)::DataFrame
|
||||
location_name,
|
||||
year,
|
||||
product_name,
|
||||
round(amount_produced[year], digits = 2),
|
||||
round(amount_produced[year], digits = 6),
|
||||
sent[year],
|
||||
disposal_amount[year],
|
||||
disposal_cost[year],
|
||||
|
||||
@@ -28,25 +28,25 @@ function plants_report(solution)::DataFrame
|
||||
for (plant_name, plant_dict) in solution["Plants"]
|
||||
for (location_name, location_dict) in plant_dict
|
||||
for year = 1:T
|
||||
capacity = round(location_dict["Capacity (tonne)"][year], digits = 2)
|
||||
received = round(location_dict["Total input (tonne)"][year], digits = 2)
|
||||
processed = round(location_dict["Process (tonne)"][year], digits = 2)
|
||||
in_storage = round(location_dict["Storage (tonne)"][year], digits = 2)
|
||||
utilization_factor = round(processed / capacity * 100.0, digits = 2)
|
||||
energy = round(location_dict["Energy (GJ)"][year], digits = 2)
|
||||
capacity = round(location_dict["Capacity (tonne)"][year], digits = 6)
|
||||
received = round(location_dict["Total input (tonne)"][year], digits = 6)
|
||||
processed = round(location_dict["Process (tonne)"][year], digits = 6)
|
||||
in_storage = round(location_dict["Storage (tonne)"][year], digits = 6)
|
||||
utilization_factor = round(processed / capacity * 100.0, digits = 6)
|
||||
energy = round(location_dict["Energy (GJ)"][year], digits = 6)
|
||||
latitude = round(location_dict["Latitude (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 =
|
||||
round(location_dict["Expansion cost (\$)"][year], digits = 2)
|
||||
round(location_dict["Expansion cost (\$)"][year], digits = 6)
|
||||
fixed_cost =
|
||||
round(location_dict["Fixed operating cost (\$)"][year], digits = 2)
|
||||
round(location_dict["Fixed operating cost (\$)"][year], digits = 6)
|
||||
var_cost =
|
||||
round(location_dict["Variable operating cost (\$)"][year], digits = 2)
|
||||
storage_cost = round(location_dict["Storage cost (\$)"][year], digits = 2)
|
||||
round(location_dict["Variable operating cost (\$)"][year], digits = 6)
|
||||
storage_cost = round(location_dict["Storage cost (\$)"][year], digits = 6)
|
||||
total_cost = round(
|
||||
opening_cost + expansion_cost + fixed_cost + var_cost + storage_cost,
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
)
|
||||
push!(
|
||||
df,
|
||||
|
||||
@@ -37,8 +37,8 @@ function products_report(solution; marginal_costs = true)::DataFrame
|
||||
longitude,
|
||||
year,
|
||||
amount,
|
||||
marginal_cost,
|
||||
amount_disposed,
|
||||
marginal_cost,
|
||||
acquisition_cost,
|
||||
disposal_cost,
|
||||
],
|
||||
|
||||
@@ -42,24 +42,24 @@ function transportation_report(solution)::DataFrame
|
||||
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||
dst_location_dict["Input product"],
|
||||
year,
|
||||
round(src_location_dict["Distance (km)"], digits = 2),
|
||||
round(src_location_dict["Distance (km)"], digits = 6),
|
||||
round(
|
||||
src_location_dict["Amount (tonne)"][year],
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
round(
|
||||
src_location_dict["Amount (tonne)"][year] *
|
||||
src_location_dict["Distance (km)"],
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
round(
|
||||
src_location_dict["Transportation cost (\$)"][year],
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
round(
|
||||
src_location_dict["Transportation energy (J)"][year] /
|
||||
1e9,
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -44,18 +44,18 @@ function transportation_emissions_report(solution)::DataFrame
|
||||
round(dst_location_dict["Longitude (deg)"], digits = 6),
|
||||
dst_location_dict["Input product"],
|
||||
year,
|
||||
round(src_location_dict["Distance (km)"], digits = 2),
|
||||
round(src_location_dict["Distance (km)"], digits = 6),
|
||||
round(
|
||||
src_location_dict["Amount (tonne)"][year],
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
round(
|
||||
src_location_dict["Amount (tonne)"][year] *
|
||||
src_location_dict["Distance (km)"],
|
||||
digits = 2,
|
||||
digits = 6,
|
||||
),
|
||||
emission_name,
|
||||
round(emission_amount[year], digits = 2),
|
||||
round(emission_amount[year], digits = 6),
|
||||
],
|
||||
)
|
||||
end
|
||||
|
||||
@@ -12,3 +12,13 @@ function write(solution::AbstractDict, filename::AbstractString)
|
||||
JSON.print(file, solution, 2)
|
||||
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_file,
|
||||
heuristic = true,
|
||||
optimizer = optimizer,
|
||||
lp_optimizer = HiGHS.Optimizer,
|
||||
return_model = true,
|
||||
|
||||
Reference in New Issue
Block a user