From ee58af73f09020aaa8a65e4a660484ff9826dabc Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Fri, 15 Oct 2021 08:14:04 -0500 Subject: [PATCH 01/12] Update sysimage and build scripts --- Makefile | 11 ++++------- src/sysimage.jl | 37 ++++++++++++++++++++++++++----------- test/model/resolve_test.jl | 4 +++- test/reports_test.jl | 4 +++- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index e86d416..5d6bfcc 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,11 @@ -JULIA := julia --color=yes --project=@. +JULIA := julia --project=. SRC_FILES := $(wildcard src/*.jl test/*.jl) VERSION := 0.5 all: docs test build/sysimage.so: src/sysimage.jl Project.toml Manifest.toml - mkdir -p build - $(JULIA) src/sysimage.jl - -build/test.log: $(SRC_FILES) build/sysimage.so - cd test; $(JULIA) --sysimage ../build/sysimage.so runtests.jl + @$(JULIA) src/sysimage.jl clean: rm -rf build/* @@ -20,7 +16,8 @@ docs: format: julia -e 'using JuliaFormatter; format(["src", "test"], verbose=true);' -test: build/test.log +test: + @$(JULIA) --sysimage build/sysimage.so test/runtests.jl test-watch: bash -c "while true; do make test --quiet; sleep 1; done" diff --git a/src/sysimage.jl b/src/sysimage.jl index 162a2fc..8cdeecf 100644 --- a/src/sysimage.jl +++ b/src/sysimage.jl @@ -1,15 +1,30 @@ using PackageCompiler +using TOML +using Logging -using Cbc -using Clp -using Geodesy -using JSON -using JSONSchema -using JuMP -using MathOptInterface -using ProgressBars +Logging.disable_logging(Logging.Info) -pkg = [:Cbc, :Clp, :Geodesy, :JSON, :JSONSchema, :JuMP, :MathOptInterface, :ProgressBars] +mkpath("build") -@info "Building system image..." -create_sysimage(pkg, sysimage_path = "build/sysimage.so") +printstyled("Generating precompilation statements...\n", color=:light_green) +run(`julia --project=. --trace-compile=build/precompile.jl $ARGS`) + +printstyled("Finding dependencies...\n", color=:light_green) +project = TOML.parsefile("Project.toml") +manifest = TOML.parsefile("Manifest.toml") +deps = Symbol[] +for dep in keys(project["deps"]) + if "path" in keys(manifest[dep][1]) + printstyled(" skip $(dep)\n", color=:light_black) + else + println(" add $(dep)") + push!(deps, Symbol(dep)) + end +end + +printstyled("Building system image...\n", color=:light_green) +create_sysimage( + deps, + precompile_statements_file = "build/precompile.jl", + sysimage_path = "build/sysimage.so", +) diff --git a/test/model/resolve_test.jl b/test/model/resolve_test.jl index f4d4e60..9021000 100644 --- a/test/model/resolve_test.jl +++ b/test/model/resolve_test.jl @@ -3,9 +3,11 @@ using RELOG +basedir = @__DIR__ + @testset "Resolve" begin # Shoud not crash - filename = "$(pwd())/../instances/s1.json" + filename = "$basedir/../../instances/s1.json" solution_old, model_old = RELOG.solve(filename, return_model = true) solution_new = RELOG.resolve(model_old, filename) end diff --git a/test/reports_test.jl b/test/reports_test.jl index 684a4cf..9193977 100644 --- a/test/reports_test.jl +++ b/test/reports_test.jl @@ -4,9 +4,11 @@ using RELOG, JSON, GZip +basedir = @__DIR__ + @testset "Reports" begin @testset "from solve" begin - solution = RELOG.solve("$(pwd())/../instances/s1.json") + solution = RELOG.solve("$basedir/../instances/s1.json") tmp_filename = tempname() # The following should not crash RELOG.write_plant_emissions_report(solution, tmp_filename) From a03b9169fde27bd9e91f77f7cef3b6ef51af54ac Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Fri, 15 Oct 2021 09:11:41 -0500 Subject: [PATCH 02/12] Allow product disposal at collection centers --- instances/s1.json | 265 ++++++++++++++++++++++++++++-------- src/docs/format.md | 6 +- src/docs/reports.md | 1 + src/graph/build.jl | 3 + src/graph/structs.jl | 1 + src/instance/parse.jl | 23 +++- src/instance/structs.jl | 3 + src/model/build.jl | 38 +++++- src/model/getsol.jl | 36 ++--- src/reports/products.jl | 3 + src/schemas/input.json | 6 + src/sysimage.jl | 8 +- test/instance/parse_test.jl | 7 + test/model/build_test.jl | 4 +- test/model/solve_test.jl | 9 ++ 15 files changed, 329 insertions(+), 84 deletions(-) diff --git a/instances/s1.json b/instances/s1.json index e88e8c5..3cbee2d 100644 --- a/instances/s1.json +++ b/instances/s1.json @@ -4,73 +4,132 @@ }, "products": { "P1": { - "transportation cost ($/km/tonne)": [0.015, 0.015], - "transportation energy (J/km/tonne)": [0.12, 0.11], + "transportation cost ($/km/tonne)": [ + 0.015, + 0.015 + ], + "transportation energy (J/km/tonne)": [ + 0.12, + 0.11 + ], "transportation emissions (tonne/km/tonne)": { - "CO2": [0.052, 0.050], - "CH4": [0.003, 0.002] - }, + "CO2": [ + 0.052, + 0.050 + ], + "CH4": [ + 0.003, + 0.002 + ] + }, "initial amounts": { "C1": { "latitude (deg)": 7.0, "longitude (deg)": 7.0, - "amount (tonne)": [934.56, 934.56] + "amount (tonne)": [ + 934.56, + 934.56 + ] }, "C2": { "latitude (deg)": 7.0, "longitude (deg)": 19.0, - "amount (tonne)": [198.95, 198.95] + "amount (tonne)": [ + 198.95, + 198.95 + ] }, "C3": { "latitude (deg)": 84.0, "longitude (deg)": 76.0, - "amount (tonne)": [212.97, 212.97] + "amount (tonne)": [ + 212.97, + 212.97 + ] }, "C4": { "latitude (deg)": 21.0, "longitude (deg)": 16.0, - "amount (tonne)": [352.19, 352.19] + "amount (tonne)": [ + 352.19, + 352.19 + ] }, "C5": { "latitude (deg)": 32.0, "longitude (deg)": 92.0, - "amount (tonne)": [510.33, 510.33] + "amount (tonne)": [ + 510.33, + 510.33 + ] }, "C6": { "latitude (deg)": 14.0, "longitude (deg)": 62.0, - "amount (tonne)": [471.66, 471.66] + "amount (tonne)": [ + 471.66, + 471.66 + ] }, "C7": { "latitude (deg)": 30.0, "longitude (deg)": 83.0, - "amount (tonne)": [785.21, 785.21] + "amount (tonne)": [ + 785.21, + 785.21 + ] }, "C8": { "latitude (deg)": 35.0, "longitude (deg)": 40.0, - "amount (tonne)": [706.17, 706.17] + "amount (tonne)": [ + 706.17, + 706.17 + ] }, "C9": { "latitude (deg)": 74.0, "longitude (deg)": 52.0, - "amount (tonne)": [30.08, 30.08] + "amount (tonne)": [ + 30.08, + 30.08 + ] }, "C10": { "latitude (deg)": 22.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": { - "transportation cost ($/km/tonne)": [0.02, 0.02] + "transportation cost ($/km/tonne)": [ + 0.02, + 0.02 + ] }, "P3": { - "transportation cost ($/km/tonne)": [0.0125, 0.0125] + "transportation cost ($/km/tonne)": [ + 0.0125, + 0.0125 + ] }, "P4": { - "transportation cost ($/km/tonne)": [0.0175, 0.0175] + "transportation cost ($/km/tonne)": [ + 0.0175, + 0.0175 + ] } }, "plants": { @@ -80,35 +139,74 @@ "P2": 0.2, "P3": 0.5 }, - "energy (GJ/tonne)": [0.12, 0.11], + "energy (GJ/tonne)": [ + 0.12, + 0.11 + ], "emissions (tonne/tonne)": { - "CO2": [0.052, 0.050], - "CH4": [0.003, 0.002] - }, + "CO2": [ + 0.052, + 0.050 + ], + "CH4": [ + 0.003, + 0.002 + ] + }, "locations": { "L1": { "latitude (deg)": 0.0, "longitude (deg)": 0.0, "disposal": { "P2": { - "cost ($/tonne)": [-10.0, -10.0], - "limit (tonne)": [1.0, 1.0] + "cost ($/tonne)": [ + -10.0, + -10.0 + ], + "limit (tonne)": [ + 1.0, + 1.0 + ] }, "P3": { - "cost ($/tonne)": [-10.0, -10.0], - "limit (tonne)": [1.0, 1.0] + "cost ($/tonne)": [ + -10.0, + -10.0 + ], + "limit (tonne)": [ + 1.0, + 1.0 + ] } }, "capacities (tonne)": { "250.0": { - "opening cost ($)": [500.0, 500.0], - "fixed operating cost ($)": [30.0, 30.0], - "variable operating cost ($/tonne)": [30.0, 30.0] + "opening cost ($)": [ + 500.0, + 500.0 + ], + "fixed operating cost ($)": [ + 30.0, + 30.0 + ], + "variable operating cost ($/tonne)": [ + 30.0, + 30.0 + ] }, "1000.0": { - "opening cost ($)": [1250.0, 1250.0], - "fixed operating cost ($)": [30.0, 30.0], - "variable operating cost ($/tonne)": [30.0, 30.0] + "opening cost ($)": [ + 1250.0, + 1250.0 + ], + "fixed operating cost ($)": [ + 30.0, + 30.0 + ], + "variable operating cost ($/tonne)": [ + 30.0, + 30.0 + ] } } }, @@ -117,17 +215,35 @@ "longitude (deg)": 0.5, "capacities (tonne)": { "0.0": { - "opening cost ($)": [1000, 1000], - "fixed operating cost ($)": [50.0, 50.0], - "variable operating cost ($/tonne)": [50.0, 50.0] + "opening cost ($)": [ + 1000, + 1000 + ], + "fixed operating cost ($)": [ + 50.0, + 50.0 + ], + "variable operating cost ($/tonne)": [ + 50.0, + 50.0 + ] }, "10000.0": { - "opening cost ($)": [10000, 10000], - "fixed operating cost ($)": [50.0, 50.0], - "variable operating cost ($/tonne)": [50.0, 50.0] + "opening cost ($)": [ + 10000, + 10000 + ], + "fixed operating cost ($)": [ + 50.0, + 50.0 + ], + "variable operating cost ($/tonne)": [ + 50.0, + 50.0 + ] } } - } + } } }, "F2": { @@ -142,14 +258,26 @@ "longitude (deg)": 65.0, "disposal": { "P3": { - "cost ($/tonne)": [100.0, 100.0] + "cost ($/tonne)": [ + 100.0, + 100.0 + ] } }, "capacities (tonne)": { "1000.0": { - "opening cost ($)": [3000, 3000], - "fixed operating cost ($)": [50.0, 50.0], - "variable operating cost ($/tonne)": [50.0, 50.0] + "opening cost ($)": [ + 3000, + 3000 + ], + "fixed operating cost ($)": [ + 50.0, + 50.0 + ], + "variable operating cost ($/tonne)": [ + 50.0, + 50.0 + ] } } }, @@ -158,9 +286,18 @@ "longitude (deg)": 0.20, "capacities (tonne)": { "10000": { - "opening cost ($)": [3000, 3000], - "fixed operating cost ($)": [50.0, 50.0], - "variable operating cost ($/tonne)": [50.0, 50.0] + "opening cost ($)": [ + 3000, + 3000 + ], + "fixed operating cost ($)": [ + 50.0, + 50.0 + ], + "variable operating cost ($/tonne)": [ + 50.0, + 50.0 + ] } } } @@ -174,12 +311,21 @@ "longitude (deg)": 100.0, "capacities (tonne)": { "15000": { - "opening cost ($)": [0.0, 0.0], - "fixed operating cost ($)": [0.0, 0.0], - "variable operating cost ($/tonne)": [-15.0, -15.0] + "opening cost ($)": [ + 0.0, + 0.0 + ], + "fixed operating cost ($)": [ + 0.0, + 0.0 + ], + "variable operating cost ($/tonne)": [ + -15.0, + -15.0 + ] } } - } + } } }, "F4": { @@ -190,12 +336,21 @@ "longitude (deg)": 50.0, "capacities (tonne)": { "10000": { - "opening cost ($)": [0.0, 0.0], - "fixed operating cost ($)": [0.0, 0.0], - "variable operating cost ($/tonne)": [-15.0, -15.0] + "opening cost ($)": [ + 0.0, + 0.0 + ], + "fixed operating cost ($)": [ + 0.0, + 0.0 + ], + "variable operating cost ($/tonne)": [ + -15.0, + -15.0 + ] } } - } + } } } } diff --git a/src/docs/format.md b/src/docs/format.md index cef0537..45d4dd2 100644 --- a/src/docs/format.md +++ b/src/docs/format.md @@ -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 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. +| `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: @@ -73,7 +75,9 @@ Each product may have some amount available at the beginning of each time period "transportation emissions (tonne/km/tonne)": { "CO2": [0.052, 0.050], "CH4": [0.003, 0.002] - } + }, + "disposal cost ($/tonne)": [-10.0, -12.0], + "disposal limit (tonne)": [1.0, 1.0], }, "P2": { "transportation cost ($/km/tonne)": [0.022, 0.020] diff --git a/src/docs/reports.md b/src/docs/reports.md index 8f7090d..2b28490 100644 --- a/src/docs/reports.md +++ b/src/docs/reports.md @@ -147,6 +147,7 @@ Report showing primary product amounts, locations and marginal costs. Generated | `longitude (deg)` | Longitude of the collection center. | `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 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. diff --git a/src/graph/build.jl b/src/graph/build.jl index ee28413..27a323e 100644 --- a/src/graph/build.jl +++ b/src/graph/build.jl @@ -18,6 +18,7 @@ function build_graph(instance::Instance)::Graph collection_shipping_nodes = ShippingNode[] name_to_process_node_map = Dict{Tuple{AbstractString,AbstractString},ProcessNode}() + collection_center_to_node = Dict() process_nodes_by_input_product = Dict(product => ProcessNode[] for product in instance.products) @@ -27,6 +28,7 @@ function build_graph(instance::Instance)::Graph for center in instance.collection_centers node = ShippingNode(next_index, center, center.product, [], []) next_index += 1 + collection_center_to_node[center] = node push!(collection_shipping_nodes, node) end @@ -83,6 +85,7 @@ function build_graph(instance::Instance)::Graph collection_shipping_nodes, arcs, name_to_process_node_map, + collection_center_to_node, ) end diff --git a/src/graph/structs.jl b/src/graph/structs.jl index ba81527..e4f1149 100644 --- a/src/graph/structs.jl +++ b/src/graph/structs.jl @@ -33,6 +33,7 @@ mutable struct Graph collection_shipping_nodes::Vector{ShippingNode} arcs::Vector{Arc} name_to_process_node_map::Dict{Tuple{AbstractString,AbstractString},ProcessNode} + collection_center_to_node::Dict{CollectionCenter,ShippingNode} end function Base.show(io::IO, instance::Graph) diff --git a/src/instance/parse.jl b/src/instance/parse.jl index 5720393..c87faa8 100644 --- a/src/instance/parse.jl +++ b/src/instance/parse.jl @@ -37,6 +37,8 @@ function parse(json)::Instance cost = product_dict["transportation cost (\$/km/tonne)"] energy = zeros(T) emissions = Dict() + disposal_limit = zeros(T) + disposal_cost = zeros(T) if "transportation energy (J/km/tonne)" in keys(product_dict) energy = product_dict["transportation energy (J/km/tonne)"] @@ -46,7 +48,25 @@ function parse(json)::Instance emissions = product_dict["transportation emissions (tonne/km/tonne)"] 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) prod_name_to_product[product_name] = product @@ -66,6 +86,7 @@ function parse(json)::Instance product, center_dict["amount (tonne)"], ) + push!(prod_centers, center) push!(collection_centers, center) end end diff --git a/src/instance/structs.jl b/src/instance/structs.jl index 190ae9b..d42ace8 100644 --- a/src/instance/structs.jl +++ b/src/instance/structs.jl @@ -13,6 +13,9 @@ mutable struct Product transportation_cost::Vector{Float64} transportation_energy::Vector{Float64} transportation_emissions::Dict{String,Vector{Float64}} + disposal_limit::Vector{Float64} + disposal_cost::Vector{Float64} + collection_centers::Vector end mutable struct CollectionCenter diff --git a/src/model/build.jl b/src/model/build.jl index 48ae7c0..1dec9d2 100644 --- a/src/model/build.jl +++ b/src/model/build.jl @@ -20,13 +20,17 @@ function create_vars!(model::JuMP.Model) graph, T = model[:graph], model[:instance].time model[:flow] = 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( model, lower_bound = 0, upper_bound = n.location.disposal_limit[n.product][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( (n, t) => @variable(model, lower_bound = 0, upper_bound = n.location.storage_limit) @@ -131,14 +135,25 @@ function create_objective_function!(model::JuMP.Model) end end - # Shipping node costs + # Plant shipping node costs for n in values(graph.plant_shipping_nodes), t = 1:T # Disposal costs add_to_expression!( obj, 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 @@ -154,16 +169,29 @@ 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] + sum(model[:flow][a, t] for a in n.outgoing_arcs) == + n.location.amount[t] + model[:collection_dispose][n, t] ) 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 for n in graph.plant_shipping_nodes @constraint( model, 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 diff --git a/src/model/getsol.jl b/src/model/getsol.jl index a368c47..f5f3e91 100644 --- a/src/model/getsol.jl +++ b/src/model/getsol.jl @@ -39,21 +39,24 @@ function get_solution(model::JuMP.Model; marginal_costs = true) end # Products - if marginal_costs - for n in graph.collection_shipping_nodes - 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, - "Longitude (deg)" => n.location.longitude, - "Amount (tonne)" => n.location.amount, - ) - if n.product.name ∉ keys(output["Products"]) - output["Products"][n.product.name] = OrderedDict() - end - output["Products"][n.product.name][n.location.name] = location_dict + for n in graph.collection_shipping_nodes + location_dict = OrderedDict{Any,Any}( + "Latitude (deg)" => n.location.latitude, + "Longitude (deg)" => n.location.longitude, + "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"]) + output["Products"][n.product.name] = OrderedDict() end + output["Products"][n.product.name][n.location.name] = location_dict end # Plants @@ -178,13 +181,14 @@ function get_solution(model::JuMP.Model; marginal_costs = true) plant_dict["Total output"][product_name] = zeros(T) 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 skip_plant = false plant_dict["Output"]["Dispose"][product_name] = disposal_dict = OrderedDict() 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["Amount (tonne)"][t] * plant.disposal_cost[shipping_node.product][t] for t = 1:T diff --git a/src/reports/products.jl b/src/reports/products.jl index 0c5f322..1d99b2c 100644 --- a/src/reports/products.jl +++ b/src/reports/products.jl @@ -13,6 +13,7 @@ function products_report(solution; marginal_costs = true)::DataFrame df."longitude (deg)" = Float64[] df."year" = Int[] df."amount (tonne)" = Float64[] + df."amount disposed (tonne)" = Float64[] df."marginal cost (\$/tonne)" = Float64[] T = length(solution["Energy"]["Plants (GJ)"]) 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) longitude = round(location_dict["Longitude (deg)"], digits = 6) amount = location_dict["Amount (tonne)"][year] + amount_disposed = location_dict["Dispose (tonne)"][year] push!( df, [ @@ -32,6 +34,7 @@ function products_report(solution; marginal_costs = true)::DataFrame year, amount, marginal_cost, + amount_disposed, ], ) end diff --git a/src/schemas/input.json b/src/schemas/input.json index fd46ec0..6ab7094 100644 --- a/src/schemas/input.json +++ b/src/schemas/input.json @@ -169,6 +169,12 @@ }, "initial amounts": { "$ref": "#/definitions/InitialAmount" + }, + "disposal limit (tonne)": { + "$ref": "#/definitions/TimeSeries" + }, + "disposal cost ($/tonne)": { + "$ref": "#/definitions/TimeSeries" } }, "required": [ diff --git a/src/sysimage.jl b/src/sysimage.jl index 8cdeecf..cb27eb0 100644 --- a/src/sysimage.jl +++ b/src/sysimage.jl @@ -6,23 +6,23 @@ Logging.disable_logging(Logging.Info) mkpath("build") -printstyled("Generating precompilation statements...\n", color=:light_green) +printstyled("Generating precompilation statements...\n", color = :light_green) run(`julia --project=. --trace-compile=build/precompile.jl $ARGS`) -printstyled("Finding dependencies...\n", color=:light_green) +printstyled("Finding dependencies...\n", color = :light_green) project = TOML.parsefile("Project.toml") manifest = TOML.parsefile("Manifest.toml") deps = Symbol[] for dep in keys(project["deps"]) if "path" in keys(manifest[dep][1]) - printstyled(" skip $(dep)\n", color=:light_black) + printstyled(" skip $(dep)\n", color = :light_black) else println(" add $(dep)") push!(deps, Symbol(dep)) end end -printstyled("Building system image...\n", color=:light_green) +printstyled("Building system image...\n", color = :light_green) create_sysimage( deps, precompile_statements_file = "build/precompile.jl", diff --git a/test/instance/parse_test.jl b/test/instance/parse_test.jl index 8c29914..f4d0527 100644 --- a/test/instance/parse_test.jl +++ b/test/instance/parse_test.jl @@ -40,7 +40,14 @@ using RELOG @test plant.sizes[2].fixed_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"] + @test p2.disposal_limit == [0.0, 0.0] + @test p2.disposal_cost == [0.0, 0.0] + p3 = product_name_to_product["P3"] @test length(plant.output) == 2 @test plant.output[p2] == 0.2 diff --git a/test/model/build_test.jl b/test/model/build_test.jl index 27d0e7f..5dc554a 100644 --- a/test/model/build_test.jl +++ b/test/model/build_test.jl @@ -18,7 +18,7 @@ using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats ) @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[:capacity]) == 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 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 upper_bound(v) == 1.0 end diff --git a/test/model/solve_test.jl b/test/model/solve_test.jl index d3b3d0d..0f03bd4 100644 --- a/test/model/solve_test.jl +++ b/test/model/solve_test.jl @@ -26,6 +26,15 @@ basedir = dirname(@__FILE__) @test "F2" in keys(solution["Plants"]) @test "F3" 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 @testset "solve (heuristic)" begin From 23b3b33146561fc5e967cd9e135467b0ede76042 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Fri, 28 Oct 2022 14:05:53 -0500 Subject: [PATCH 03/12] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0eff1c1..1d66f57 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ * **Nwike Iloeje** <> * **John Atkins** * **Kyle Sun** +* **Audrey Gallier** ### License From 48bd3c403f88ea4fe56bbd938f4f358c010eaea9 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Thu, 15 Dec 2022 09:49:38 -0600 Subject: [PATCH 04/12] Switch from Euclidean to approximate driving distance --- CHANGELOG.md | 4 +++ Project.toml | 3 +- docs/src/usage.md | 2 +- src/RELOG.jl | 6 ++-- src/graph/build.jl | 12 ++----- src/graph/dist.jl | 69 ++++++++++++++++++++++++++++++++++++++++ test/graph/build_test.jl | 2 +- test/graph/dist_test.jl | 25 +++++++++++++++ test/runtests.jl | 1 + 9 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 src/graph/dist.jl create mode 100644 test/graph/dist_test.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index f3776ff..5f45517 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ 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.6.0] -- 2022-12-15 +### Changed +- Switch from Euclidean distance to approximate driving distance + ## [0.5.2] -- 2022-08-26 ### Changed - Update to JuMP 1.x diff --git a/Project.toml b/Project.toml index 6a4eab8..038f3c2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "RELOG" uuid = "a2afcdf7-cf04-4913-85f9-c0d81ddf2008" authors = ["Alinson S Xavier "] -version = "0.5.2" +version = "0.6.0" [deps] CRC = "44b605c4-b955-5f2b-9b6d-d2bd01d3d205" @@ -18,6 +18,7 @@ JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" JuMP = "4076af6c-e467-56ae-b986-b466b2749572" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" ProgressBars = "49802e3a-d2f1-5c88-81d8-b72133a6f568" diff --git a/docs/src/usage.md b/docs/src/usage.md index 9c1e384..7a2fe25 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -7,7 +7,7 @@ To use RELOG, the first step is to install the [Julia programming language](http ```julia using Pkg -Pkg.add(name="RELOG", version="0.5") +Pkg.add(name="RELOG", version="0.6") ``` After the package and all its dependencies have been installed, please run the RELOG test suite, as shown below, to make sure that the package has been correctly installed: diff --git a/src/RELOG.jl b/src/RELOG.jl index d8d4226..0f39e32 100644 --- a/src/RELOG.jl +++ b/src/RELOG.jl @@ -5,19 +5,19 @@ module RELOG include("instance/structs.jl") - include("graph/structs.jl") +include("instance/geodb.jl") +include("graph/dist.jl") include("graph/build.jl") include("graph/csv.jl") include("instance/compress.jl") -include("instance/geodb.jl") include("instance/parse.jl") include("instance/validate.jl") include("model/build.jl") include("model/getsol.jl") -include("model/solve.jl") include("model/resolve.jl") +include("model/solve.jl") include("reports/plant_emissions.jl") include("reports/plant_outputs.jl") include("reports/plants.jl") diff --git a/src/graph/build.jl b/src/graph/build.jl index dc274e7..12640d6 100644 --- a/src/graph/build.jl +++ b/src/graph/build.jl @@ -2,14 +2,6 @@ # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -using Geodesy - -function calculate_distance(source_lat, source_lon, dest_lat, dest_lon)::Float64 - x = LLA(source_lat, source_lon, 0.0) - y = LLA(dest_lat, dest_lon, 0.0) - return round(euclidean_distance(x, y) / 1000.0, digits = 2) -end - function build_graph(instance::Instance)::Graph arcs = [] next_index = 0 @@ -48,13 +40,15 @@ function build_graph(instance::Instance)::Graph end # Build arcs from collection centers to plants, and from one plant to another + metric = _KnnDrivingDistance() for source in [collection_shipping_nodes; plant_shipping_nodes] for dest in process_nodes_by_input_product[source.product] - distance = calculate_distance( + distance = _calculate_distance( source.location.latitude, source.location.longitude, dest.location.latitude, dest.location.longitude, + metric, ) values = Dict("distance" => distance) arc = Arc(source, dest, values) diff --git a/src/graph/dist.jl b/src/graph/dist.jl new file mode 100644 index 0000000..abaecc0 --- /dev/null +++ b/src/graph/dist.jl @@ -0,0 +1,69 @@ +# RELOG: Reverse Logistics Optimization +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using Geodesy +using NearestNeighbors +using DataFrames + +Base.@kwdef mutable struct _KnnDrivingDistance + tree = nothing + ratios = nothing +end + +mutable struct _EuclideanDistance end + +function _calculate_distance( + source_lat, + source_lon, + dest_lat, + dest_lon, + ::_EuclideanDistance, +)::Float64 + x = LLA(source_lat, source_lon, 0.0) + y = LLA(dest_lat, dest_lon, 0.0) + return round(euclidean_distance(x, y) / 1000.0, digits = 3) +end + +function _calculate_distance( + source_lat, + source_lon, + dest_lat, + dest_lon, + metric::_KnnDrivingDistance, +)::Float64 + if metric.tree === nothing + basedir = joinpath(dirname(@__FILE__), "..", "..", "data") + csv_filename = joinpath(basedir, "dist_driving.csv") + + # Download pre-computed driving data + if !isfile(csv_filename) + _download_zip( + "https://axavier.org/RELOG/0.6/data/dist_driving_0b9a6ad6.zip", + basedir, + csv_filename, + 0x0b9a6ad6, + ) + end + + # Fit kNN model + df = DataFrame(CSV.File(csv_filename)) + coords = Matrix(df[!, [:source_lat, :source_lon, :dest_lat, :dest_lon]])' + metric.ratios = Matrix(df[!, [:ratio]]) + metric.tree = KDTree(coords) + end + + # Compute Euclidean distance + dist_euclidean = _calculate_distance( + source_lat, + source_lon, + dest_lat, + dest_lon, + _EuclideanDistance(), + ) + + # Predict ratio + idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5) + ratio_pred = mean(metric.ratios[idxs]) + return round(dist_euclidean * ratio_pred, digits = 3) +end diff --git a/test/graph/build_test.jl b/test/graph/build_test.jl index 1b52f58..9df3cab 100644 --- a/test/graph/build_test.jl +++ b/test/graph/build_test.jl @@ -21,7 +21,7 @@ using RELOG @test node.outgoing_arcs[1].source.location.name == "C1" @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].values["distance"] == 1095.62 + @test node.outgoing_arcs[1].values["distance"] == 1695.364 node = process_node_by_location_name["L1"] @test node.location.plant_name == "F1" diff --git a/test/graph/dist_test.jl b/test/graph/dist_test.jl new file mode 100644 index 0000000..cce8b6a --- /dev/null +++ b/test/graph/dist_test.jl @@ -0,0 +1,25 @@ +# RELOG: Reverse Logistics Optimization +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using RELOG + +@testset "KnnDrivingDistance" begin + # Euclidean distance between Chicago and Indianapolis + @test RELOG._calculate_distance( + 41.866, + -87.656, + 39.764, + -86.148, + RELOG._EuclideanDistance(), + ) == 265.818 + + # Approximate driving distance between Chicago and Indianapolis + @test RELOG._calculate_distance( + 41.866, + -87.656, + 39.764, + -86.148, + RELOG._KnnDrivingDistance(), + ) == 316.43 +end diff --git a/test/runtests.jl b/test/runtests.jl index 3fcf070..1da75c7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,6 +11,7 @@ using Test end @testset "Graph" begin include("graph/build_test.jl") + include("graph/dist_test.jl") end @testset "Model" begin include("model/build_test.jl") From 841fbf16fb6c9696246655d19f37a3844ca60105 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Thu, 15 Dec 2022 10:21:21 -0600 Subject: [PATCH 05/12] Make distance metric configurable; fix building period bug --- CHANGELOG.md | 7 +++++-- docs/src/format.md | 5 ++++- instances/s1.json | 3 ++- src/graph/build.jl | 3 +-- src/graph/dist.jl | 20 ++++---------------- src/instance/parse.jl | 26 +++++++++++++++++++++++--- src/instance/structs.jl | 11 +++++++++++ src/schemas/input.json | 3 +++ test/graph/dist_test.jl | 4 ++-- 9 files changed, 55 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f45517..6aa093c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,11 @@ All notable changes to this project will be documented in this file. [pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0 # [0.6.0] -- 2022-12-15 -### Changed -- Switch from Euclidean distance to approximate driving distance +### Added +- Allow RELOG to calculate approximate driving distances, instead of just straight-line distances between points. + +### Fixed +- Fix bug that caused building period parameter to be ignored ## [0.5.2] -- 2022-08-26 ### Changed diff --git a/docs/src/format.md b/docs/src/format.md index 01fdc7d..4164370 100644 --- a/docs/src/format.md +++ b/docs/src/format.md @@ -14,6 +14,7 @@ The **parameters** section describes details about the simulation itself. |:--------------------------|:---------------| |`time horizon (years)` | Number of years in the simulation. |`building period (years)` | List of years in which we are allowed to open new plants. For example, if this parameter is set to `[1,2,3]`, we can only open plants during the first three years. By default, this equals `[1]`; that is, plants can only be opened during the first year. | +|`distance metric` | Metric used to compute distances between pairs of locations. Valid options are: `"Euclidean"`, for the straight-line distance between points; or `"driving"` for an approximated driving distance. If not specified, defaults to `"Euclidean"`. #### Example @@ -21,7 +22,8 @@ The **parameters** section describes details about the simulation itself. { "parameters": { "time horizon (years)": 2, - "building period (years)": [1] + "building period (years)": [1], + "distance metric": "driving", } } ``` @@ -220,6 +222,7 @@ Database | Description | Examples * Plants can be expanded at any time, even long after they are open. * All material available at the beginning of a time period must be entirely processed by the end of that time period. It is not possible to store unprocessed materials from one time period to the next. * Up to two plant sizes are currently supported. Variable operating costs must be the same for all plant sizes. +* Accurate driving distances are only available for the continental United States. ## Output Data Format (JSON) diff --git a/instances/s1.json b/instances/s1.json index e88e8c5..279f1ae 100644 --- a/instances/s1.json +++ b/instances/s1.json @@ -1,6 +1,7 @@ { "parameters": { - "time horizon (years)": 2 + "time horizon (years)": 2, + "distance metric": "driving" }, "products": { "P1": { diff --git a/src/graph/build.jl b/src/graph/build.jl index 12640d6..418ac5e 100644 --- a/src/graph/build.jl +++ b/src/graph/build.jl @@ -40,7 +40,6 @@ function build_graph(instance::Instance)::Graph end # Build arcs from collection centers to plants, and from one plant to another - metric = _KnnDrivingDistance() for source in [collection_shipping_nodes; plant_shipping_nodes] for dest in process_nodes_by_input_product[source.product] distance = _calculate_distance( @@ -48,7 +47,7 @@ function build_graph(instance::Instance)::Graph source.location.longitude, dest.location.latitude, dest.location.longitude, - metric, + instance.distance_metric, ) values = Dict("distance" => distance) arc = Arc(source, dest, values) diff --git a/src/graph/dist.jl b/src/graph/dist.jl index abaecc0..777ad87 100644 --- a/src/graph/dist.jl +++ b/src/graph/dist.jl @@ -6,19 +6,12 @@ using Geodesy using NearestNeighbors using DataFrames -Base.@kwdef mutable struct _KnnDrivingDistance - tree = nothing - ratios = nothing -end - -mutable struct _EuclideanDistance end - function _calculate_distance( source_lat, source_lon, dest_lat, dest_lon, - ::_EuclideanDistance, + ::EuclideanDistance, )::Float64 x = LLA(source_lat, source_lon, 0.0) y = LLA(dest_lat, dest_lon, 0.0) @@ -30,7 +23,7 @@ function _calculate_distance( source_lon, dest_lat, dest_lon, - metric::_KnnDrivingDistance, + metric::KnnDrivingDistance, )::Float64 if metric.tree === nothing basedir = joinpath(dirname(@__FILE__), "..", "..", "data") @@ -54,13 +47,8 @@ function _calculate_distance( end # Compute Euclidean distance - dist_euclidean = _calculate_distance( - source_lat, - source_lon, - dest_lat, - dest_lon, - _EuclideanDistance(), - ) + dist_euclidean = + _calculate_distance(source_lat, source_lon, dest_lat, dest_lon, EuclideanDistance()) # Predict ratio idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5) diff --git a/src/instance/parse.jl b/src/instance/parse.jl index 5720393..d105aee 100644 --- a/src/instance/parse.jl +++ b/src/instance/parse.jl @@ -23,10 +23,23 @@ function parse(json)::Instance validate(json, Schema(json_schema)) building_period = [1] - if "building period (years)" in keys(json) - building_period = json["building period (years)"] + if "building period (years)" in keys(json["parameters"]) + building_period = json["parameters"]["building period (years)"] end + distance_metric = EuclideanDistance() + if "distance metric" in keys(json["parameters"]) + metric_name = json["parameters"]["distance metric"] + if metric_name == "driving" + distance_metric = KnnDrivingDistance() + elseif metric_name == "Euclidean" + # nop + else + error("Unknown distance metric: $metric_name") + end + end + @show distance_metric + plants = Plant[] products = Product[] collection_centers = CollectionCenter[] @@ -176,5 +189,12 @@ function parse(json)::Instance @info @sprintf("%12d collection centers", length(collection_centers)) @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, + distance_metric, + ) end diff --git a/src/instance/structs.jl b/src/instance/structs.jl index 190ae9b..692fbca 100644 --- a/src/instance/structs.jl +++ b/src/instance/structs.jl @@ -48,10 +48,21 @@ mutable struct Plant storage_cost::Vector{Float64} end + +abstract type DistanceMetric end + +Base.@kwdef mutable struct KnnDrivingDistance <: DistanceMetric + tree = nothing + ratios = nothing +end + +mutable struct EuclideanDistance <: DistanceMetric end + mutable struct Instance time::Int64 products::Vector{Product} collection_centers::Vector{CollectionCenter} plants::Vector{Plant} building_period::Vector{Int64} + distance_metric::DistanceMetric end diff --git a/src/schemas/input.json b/src/schemas/input.json index fd46ec0..1cafb71 100644 --- a/src/schemas/input.json +++ b/src/schemas/input.json @@ -14,6 +14,9 @@ "properties": { "time horizon (years)": { "type": "number" + }, + "distance metric": { + "type": "string" } }, "required": [ diff --git a/test/graph/dist_test.jl b/test/graph/dist_test.jl index cce8b6a..d643602 100644 --- a/test/graph/dist_test.jl +++ b/test/graph/dist_test.jl @@ -11,7 +11,7 @@ using RELOG -87.656, 39.764, -86.148, - RELOG._EuclideanDistance(), + RELOG.EuclideanDistance(), ) == 265.818 # Approximate driving distance between Chicago and Indianapolis @@ -20,6 +20,6 @@ using RELOG -87.656, 39.764, -86.148, - RELOG._KnnDrivingDistance(), + RELOG.KnnDrivingDistance(), ) == 316.43 end From 9191474df83df1e618a74b66c6609c2af78e9117 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Thu, 15 Dec 2022 10:36:44 -0600 Subject: [PATCH 06/12] Bump version to 0.6 --- Makefile | 2 +- README.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 0b8ace3..e95cd0a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION := 0.5 +VERSION := 0.6 clean: rm -rfv build Manifest.toml test/Manifest.toml deps/formatter/build deps/formatter/Manifest.toml diff --git a/README.md b/README.md index 1d66f57..a581688 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,14 @@ - + ### Documentation - * [Usage](https://anl-ceeesa.github.io/RELOG/0.5/usage) - * [Input and Output Data Formats](https://anl-ceeesa.github.io/RELOG/0.5/format) - * [Simplified Solution Reports](https://anl-ceeesa.github.io/RELOG/0.5/reports) - * [Optimization Model](https://anl-ceeesa.github.io/RELOG/0.5/model) + * [Usage](https://anl-ceeesa.github.io/RELOG/0.6/usage) + * [Input and Output Data Formats](https://anl-ceeesa.github.io/RELOG/0.6/format) + * [Simplified Solution Reports](https://anl-ceeesa.github.io/RELOG/0.6/reports) + * [Optimization Model](https://anl-ceeesa.github.io/RELOG/0.6/model) ### Authors From 51ff8eb1301af38229f30c62380387fb1ada2b17 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Thu, 15 Dec 2022 11:03:44 -0600 Subject: [PATCH 07/12] Restrict NearestNeighbors version; remove debug statement --- Project.toml | 1 + src/instance/parse.jl | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 038f3c2..baacbfd 100644 --- a/Project.toml +++ b/Project.toml @@ -45,3 +45,4 @@ ProgressBars = "1" Shapefile = "0.8" ZipFile = "0.10" julia = "1" +NearestNeighbors = "0.4" diff --git a/src/instance/parse.jl b/src/instance/parse.jl index d105aee..255af28 100644 --- a/src/instance/parse.jl +++ b/src/instance/parse.jl @@ -38,7 +38,6 @@ function parse(json)::Instance error("Unknown distance metric: $metric_name") end end - @show distance_metric plants = Plant[] products = Product[] From d1f6796c96a56ed84dce41d748dedb8bc82fb1fa Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Fri, 16 Dec 2022 15:33:16 -0600 Subject: [PATCH 08/12] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa093c..a257a56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ 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.6.0] -- 2022-12-15 +## [0.6.0] -- 2022-12-15 ### Added - Allow RELOG to calculate approximate driving distances, instead of just straight-line distances between points. From 79748e3c13390e39306be7373c0536c43bf8b117 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Tue, 24 Jan 2023 10:27:41 -0600 Subject: [PATCH 09/12] Dist: Drop NaN in training dataset --- src/graph/dist.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/graph/dist.jl b/src/graph/dist.jl index 777ad87..a5e1abf 100644 --- a/src/graph/dist.jl +++ b/src/graph/dist.jl @@ -40,7 +40,8 @@ function _calculate_distance( end # Fit kNN model - df = DataFrame(CSV.File(csv_filename)) + df = DataFrame(CSV.File(csv_filename, missingstring="NaN")) + dropmissing!(df) coords = Matrix(df[!, [:source_lat, :source_lon, :dest_lat, :dest_lon]])' metric.ratios = Matrix(df[!, [:ratio]]) metric.tree = KDTree(coords) @@ -53,5 +54,7 @@ function _calculate_distance( # Predict ratio idxs, _ = knn(metric.tree, [source_lat, source_lon, dest_lat, dest_lon], 5) ratio_pred = mean(metric.ratios[idxs]) - return round(dist_euclidean * ratio_pred, digits = 3) + dist_pred = round(dist_euclidean * ratio_pred, digits = 3) + isfinite(dist_pred) || error("non-finite distance detected: $dist_pred") + return dist_pred end From 50d53f628fb979f28c28d52a86f20f0db2a797f4 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Tue, 24 Jan 2023 10:31:40 -0600 Subject: [PATCH 10/12] Reformat source code --- src/graph/dist.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph/dist.jl b/src/graph/dist.jl index a5e1abf..05fe687 100644 --- a/src/graph/dist.jl +++ b/src/graph/dist.jl @@ -40,7 +40,7 @@ function _calculate_distance( end # Fit kNN model - df = DataFrame(CSV.File(csv_filename, missingstring="NaN")) + df = DataFrame(CSV.File(csv_filename, missingstring = "NaN")) dropmissing!(df) coords = Matrix(df[!, [:source_lat, :source_lon, :dest_lat, :dest_lon]])' metric.ratios = Matrix(df[!, [:ratio]]) From 22d73c9ded10d8164c89656f766e233572da35d5 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Wed, 15 Feb 2023 11:53:28 -0600 Subject: [PATCH 11/12] Move tests to a separate package; update GitHub CI and docs --- .github/workflows/lint.yml | 4 +- .github/workflows/test.yml | 14 ++- deps/formatter/Project.toml | 5 - deps/formatter/format.jl | 8 -- docs/src/usage.md | 6 -- juliaw | 75 -------------- mkdocs.yml | 23 ----- test/Project.toml | 19 ++++ {instances => test/fixtures}/s1.json | 0 test/fixtures/s1.zip | Bin 0 -> 1089 bytes {instances => test/fixtures}/s2.json | 0 .../fixtures}/solutions/s1.json | 0 {instances => test/fixtures}/solutions/s1.log | 0 test/graph/build_test.jl | 39 -------- test/graph/dist_test.jl | 25 ----- test/instance/compress_test.jl | 53 ---------- test/instance/geodb_test.jl | 25 ----- test/instance/parse_test.jl | 93 ------------------ test/model/build_test.jl | 38 ------- test/model/resolve_test.jl | 13 --- test/model/solve_test.jl | 70 ------------- test/reports_test.jl | 21 ---- test/runtests.jl | 22 ----- test/src/RELOGT.jl | 51 ++++++++++ test/src/graph/build_test.jl | 40 ++++++++ test/src/graph/dist_test.jl | 27 +++++ test/src/instance/compress_test.jl | 54 ++++++++++ test/src/instance/geodb_test.jl | 27 +++++ test/src/instance/parse_test.jl | 87 ++++++++++++++++ test/src/model/build_test.jl | 40 ++++++++ test/src/model/resolve_test.jl | 13 +++ test/src/model/solve_test.jl | 70 +++++++++++++ test/src/reports_test.jl | 21 ++++ 33 files changed, 463 insertions(+), 520 deletions(-) delete mode 100644 deps/formatter/Project.toml delete mode 100644 deps/formatter/format.jl delete mode 100755 juliaw delete mode 100644 mkdocs.yml create mode 100644 test/Project.toml rename {instances => test/fixtures}/s1.json (100%) create mode 100644 test/fixtures/s1.zip rename {instances => test/fixtures}/s2.json (100%) rename {instances => test/fixtures}/solutions/s1.json (100%) rename {instances => test/fixtures}/solutions/s1.log (100%) delete mode 100644 test/graph/build_test.jl delete mode 100644 test/graph/dist_test.jl delete mode 100644 test/instance/compress_test.jl delete mode 100644 test/instance/geodb_test.jl delete mode 100644 test/instance/parse_test.jl delete mode 100644 test/model/build_test.jl delete mode 100644 test/model/resolve_test.jl delete mode 100644 test/model/solve_test.jl delete mode 100644 test/reports_test.jl delete mode 100644 test/runtests.jl create mode 100644 test/src/RELOGT.jl create mode 100644 test/src/graph/build_test.jl create mode 100644 test/src/graph/dist_test.jl create mode 100644 test/src/instance/compress_test.jl create mode 100644 test/src/instance/geodb_test.jl create mode 100644 test/src/instance/parse_test.jl create mode 100644 test/src/model/build_test.jl create mode 100644 test/src/model/resolve_test.jl create mode 100644 test/src/model/solve_test.jl create mode 100644 test/src/reports_test.jl diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b71acf6..327c0ef 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,10 +14,10 @@ jobs: shell: julia --color=yes {0} run: | using Pkg - Pkg.add(PackageSpec(name="JuliaFormatter", version="0.14.4")) + Pkg.add(PackageSpec(name="JuliaFormatter", version="1")) using JuliaFormatter format("src", verbose=true) - format("test", verbose=true) + format("test/src", verbose=true) out = String(read(Cmd(`git diff`))) if isempty(out) exit(0) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5aa8f86..e2c31cf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,5 +21,15 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 + - name: Run tests + shell: julia --color=yes --project=test {0} + run: | + using Pkg + Pkg.develop(path=".") + Pkg.update() + using RELOGT + try + runtests() + catch + exit(1) + end diff --git a/deps/formatter/Project.toml b/deps/formatter/Project.toml deleted file mode 100644 index 4bc5f25..0000000 --- a/deps/formatter/Project.toml +++ /dev/null @@ -1,5 +0,0 @@ -[deps] -JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" - -[compat] -JuliaFormatter = "0.14.4" diff --git a/deps/formatter/format.jl b/deps/formatter/format.jl deleted file mode 100644 index 9f9d965..0000000 --- a/deps/formatter/format.jl +++ /dev/null @@ -1,8 +0,0 @@ -using JuliaFormatter -format( - [ - "../../src", - "../../test", - ], - verbose=true, -) diff --git a/docs/src/usage.md b/docs/src/usage.md index 7a2fe25..0a05396 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -10,12 +10,6 @@ using Pkg Pkg.add(name="RELOG", version="0.6") ``` -After the package and all its dependencies have been installed, please run the RELOG test suite, as shown below, to make sure that the package has been correctly installed: - -```julia -Pkg.test("RELOG") -``` - ## 2. Modeling the problem The two main model components in RELOG are **products** and **plants**. diff --git a/juliaw b/juliaw deleted file mode 100755 index b78bc72..0000000 --- a/juliaw +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment -# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved. -# Released under the modified BSD license. See COPYING.md for more details. - -if [ ! -e Project.toml ]; then - echo "juliaw: Project.toml not found" - exit 1 -fi - -if [ ! -e Manifest.toml ]; then - julia --project=. -e 'using Pkg; Pkg.instantiate()' || exit 1 -fi - -if [ ! -e build/sysimage.so -o Project.toml -nt build/sysimage.so ]; then - echo "juliaw: rebuilding system image..." - - # Generate temporary project folder - rm -rf $HOME/.juliaw - mkdir -p $HOME/.juliaw/src - cp Project.toml Manifest.toml $HOME/.juliaw - NAME=$(julia -e 'using TOML; toml = TOML.parsefile("Project.toml"); "name" in keys(toml) && print(toml["name"])') - if [ ! -z $NAME ]; then - cat > $HOME/.juliaw/src/$NAME.jl << EOF -module $NAME -end -EOF - fi - - # Add PackageCompiler dependencies to temporary project - julia --project=$HOME/.juliaw -e 'using Pkg; Pkg.add(["PackageCompiler", "TOML", "Logging"])' - - # Generate system image scripts - cat > $HOME/.juliaw/sysimage.jl << EOF -using PackageCompiler -using TOML -using Logging - -Logging.disable_logging(Logging.Info) -mkpath("$PWD/build") - -println("juliaw: generating precompilation statements...") -run(\`julia --project="$PWD" --trace-compile="$PWD"/build/precompile.jl \$(ARGS)\`) - -println("juliaw: finding dependencies...") -project = TOML.parsefile("Project.toml") -manifest = TOML.parsefile("Manifest.toml") -deps = Symbol[] -for dep in keys(project["deps"]) - if dep in keys(manifest) - # Up to Julia 1.6 - dep_entry = manifest[dep][1] - else - # Julia 1.7+ - dep_entry = manifest["deps"][dep][1] - end - if "path" in keys(dep_entry) - println(" - \$(dep) [skip]") - else - println(" - \$(dep)") - push!(deps, Symbol(dep)) - end -end - -println("juliaw: building system image...") -create_sysimage( - deps, - precompile_statements_file = "$PWD/build/precompile.jl", - sysimage_path = "$PWD/build/sysimage.so", -) -EOF - julia --project=$HOME/.juliaw $HOME/.juliaw/sysimage.jl $* -else - julia --project=. --sysimage build/sysimage.so $* -fi diff --git a/mkdocs.yml b/mkdocs.yml deleted file mode 100644 index 551535f..0000000 --- a/mkdocs.yml +++ /dev/null @@ -1,23 +0,0 @@ -site_name: RELOG -theme: cinder -copyright: "Copyright © 2020, UChicago Argonne, LLC. All Rights Reserved." -repo_url: https://github.com/ANL-CEEESA/RELOG -edit_uri: edit/master/src/docs/ -nav: - - Home: index.md - - Usage: usage.md - - Data Format: format.md - - Reports: reports.md - - Optimization Model: model.md -plugins: - - search -markdown_extensions: - - admonition - - mdx_math -extra_javascript: - - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML - - js/mathjax.js -docs_dir: src/docs -site_dir: docs -extra_css: - - "css/custom.css" diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..bcf66c4 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,19 @@ +name = "RELOGT" +uuid = "a6dae211-05d8-42ed-9081-b88c982fc90a" +authors = ["Alinson S. Xavier "] +version = "0.1.0" + +[deps] +Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76" +GZip = "92fee26a-97fe-5a0c-ad85-20a5f3185b63" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +JuMP = "4076af6c-e467-56ae-b986-b466b2749572" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +RELOG = "a2afcdf7-cf04-4913-85f9-c0d81ddf2008" +Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[compat] +JuliaFormatter = "1" \ No newline at end of file diff --git a/instances/s1.json b/test/fixtures/s1.json similarity index 100% rename from instances/s1.json rename to test/fixtures/s1.json diff --git a/test/fixtures/s1.zip b/test/fixtures/s1.zip new file mode 100644 index 0000000000000000000000000000000000000000..2d09afb79d024c323b67281c6be4b5e46c813d65 GIT binary patch literal 1089 zcmWIWW@Zs#-~d8>PxD{~D7YfSz`)L+z))wl2$GSGQ)}Dx1@nZhaAN z;(RK1>CriBE4#Cok_!8mna6TZxyq(tYg(szI_R&)#gP8InVHVl58tm(;&oc5t}v%0 z%^*v@*{%0SRPTn})2>~!uE}zqGIPz62&Iju%p;F4|1J4zU+tg#mDj>@pJ~oIciDCC z1dhpe9*bwCbBD(^FE5-@R{V&4M!THRr*-Bz*~PrSy$@tEIrvSMP}e%%Q{wWDdBSnV zBhM8XtQPaAPi*fgX^~@CV9fQ=ie=Y%$z`1&fpd%(YS|j*@o0ZvSnRc7{kKgM`z|;$ zlyG^@5MZ+H(BQkOXL?DnishpfqeFi!!xxQbGXnV~Uvk*CIXq{0aqxqc?gh_|mA96; zSvmPDY!#ejAT!CJ)F#=#-L#Cwk8{ZZhcZq1xMiu+e<`k$-aJw4jrh0a&8EI~-=(K$ zXB$p=kZ^C>-MZfeY0hgFI`{D3cz3-#{dQoowbhopY31G*pDIqy=H5Q}W>xUY>!u%H zUszWYyz;+X(dgDcOB8>)mL-$e)^An)~!F?_0RKY<x#KuST8E zRM~eg;a~lp{W-oYMe42vEH9OEG`Nqud^KJ1ZM$Ck%k0w&%i~p>^QPK_?ocRiT&A(( z*%`y!f=eGRdM-X(tTwY`>%%Dq)2GaS%vb-)?)4x=78V0sBf>=c6@&IpEHjr{g OAPfc4*MOEWFaQ7*0NhUi literal 0 HcmV?d00001 diff --git a/instances/s2.json b/test/fixtures/s2.json similarity index 100% rename from instances/s2.json rename to test/fixtures/s2.json diff --git a/instances/solutions/s1.json b/test/fixtures/solutions/s1.json similarity index 100% rename from instances/solutions/s1.json rename to test/fixtures/solutions/s1.json diff --git a/instances/solutions/s1.log b/test/fixtures/solutions/s1.log similarity index 100% rename from instances/solutions/s1.log rename to test/fixtures/solutions/s1.log diff --git a/test/graph/build_test.jl b/test/graph/build_test.jl deleted file mode 100644 index 9df3cab..0000000 --- a/test/graph/build_test.jl +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG - -@testset "build_graph" begin - basedir = dirname(@__FILE__) - instance = RELOG.parsefile("$basedir/../../instances/s1.json") - graph = RELOG.build_graph(instance) - process_node_by_location_name = - Dict(n.location.location_name => n for n in graph.process_nodes) - - @test length(graph.plant_shipping_nodes) == 8 - @test length(graph.collection_shipping_nodes) == 10 - @test length(graph.process_nodes) == 6 - - node = graph.collection_shipping_nodes[1] - @test node.location.name == "C1" - @test length(node.incoming_arcs) == 0 - @test length(node.outgoing_arcs) == 2 - @test node.outgoing_arcs[1].source.location.name == "C1" - @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].values["distance"] == 1695.364 - - node = process_node_by_location_name["L1"] - @test node.location.plant_name == "F1" - @test node.location.location_name == "L1" - @test length(node.incoming_arcs) == 10 - @test length(node.outgoing_arcs) == 2 - - node = process_node_by_location_name["L3"] - @test node.location.plant_name == "F2" - @test node.location.location_name == "L3" - @test length(node.incoming_arcs) == 2 - @test length(node.outgoing_arcs) == 2 - - @test length(graph.arcs) == 38 -end diff --git a/test/graph/dist_test.jl b/test/graph/dist_test.jl deleted file mode 100644 index d643602..0000000 --- a/test/graph/dist_test.jl +++ /dev/null @@ -1,25 +0,0 @@ -# RELOG: Reverse Logistics Optimization -# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. -# Released under the modified BSD license. See COPYING.md for more details. - -using RELOG - -@testset "KnnDrivingDistance" begin - # Euclidean distance between Chicago and Indianapolis - @test RELOG._calculate_distance( - 41.866, - -87.656, - 39.764, - -86.148, - RELOG.EuclideanDistance(), - ) == 265.818 - - # Approximate driving distance between Chicago and Indianapolis - @test RELOG._calculate_distance( - 41.866, - -87.656, - 39.764, - -86.148, - RELOG.KnnDrivingDistance(), - ) == 316.43 -end diff --git a/test/instance/compress_test.jl b/test/instance/compress_test.jl deleted file mode 100644 index 87d2a98..0000000 --- a/test/instance/compress_test.jl +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG - -@testset "compress" begin - basedir = dirname(@__FILE__) - instance = RELOG.parsefile("$basedir/../../instances/s1.json") - compressed = RELOG._compress(instance) - - product_name_to_product = Dict(p.name => p for p in compressed.products) - location_name_to_facility = Dict() - for p in compressed.plants - location_name_to_facility[p.location_name] = p - end - for c in compressed.collection_centers - location_name_to_facility[c.name] = c - end - - p1 = product_name_to_product["P1"] - p2 = product_name_to_product["P2"] - p3 = product_name_to_product["P3"] - c1 = location_name_to_facility["C1"] - l1 = location_name_to_facility["L1"] - - @test compressed.time == 1 - @test compressed.building_period == [1] - - @test p1.name == "P1" - @test p1.transportation_cost ≈ [0.015] - @test p1.transportation_energy ≈ [0.115] - @test p1.transportation_emissions["CO2"] ≈ [0.051] - @test p1.transportation_emissions["CH4"] ≈ [0.0025] - - @test c1.name == "C1" - @test c1.amount ≈ [1869.12] - - @test l1.plant_name == "F1" - @test l1.location_name == "L1" - @test l1.energy ≈ [0.115] - @test l1.emissions["CO2"] ≈ [0.051] - @test l1.emissions["CH4"] ≈ [0.0025] - @test l1.sizes[1].opening_cost ≈ [500] - @test l1.sizes[2].opening_cost ≈ [1250] - @test l1.sizes[1].fixed_operating_cost ≈ [60] - @test l1.sizes[2].fixed_operating_cost ≈ [60] - @test l1.sizes[1].variable_operating_cost ≈ [30] - @test l1.sizes[2].variable_operating_cost ≈ [30] - @test l1.disposal_limit[p2] ≈ [2.0] - @test l1.disposal_limit[p3] ≈ [2.0] - @test l1.disposal_cost[p2] ≈ [-10.0] - @test l1.disposal_cost[p3] ≈ [-10.0] -end diff --git a/test/instance/geodb_test.jl b/test/instance/geodb_test.jl deleted file mode 100644 index e0009f0..0000000 --- a/test/instance/geodb_test.jl +++ /dev/null @@ -1,25 +0,0 @@ -# RELOG: Reverse Logistics Optimization -# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. -# Released under the modified BSD license. See COPYING.md for more details. - -using RELOG - -@testset "geodb_query (2018-us-county)" begin - region = RELOG.geodb_query("2018-us-county:17043") - @test region.centroid.lat == 41.83956 - @test region.centroid.lon == -88.08857 - @test region.population == 922_921 -end - -# @testset "geodb_query (2018-us-zcta)" begin -# region = RELOG.geodb_query("2018-us-zcta:60439") -# @test region.centroid.lat == 41.68241 -# @test region.centroid.lon == -87.98954 -# end - -@testset "geodb_query (us-state)" begin - region = RELOG.geodb_query("us-state:IL") - @test region.centroid.lat == 39.73939 - @test region.centroid.lon == -89.50414 - @test region.population == 12_671_821 -end diff --git a/test/instance/parse_test.jl b/test/instance/parse_test.jl deleted file mode 100644 index f4d0527..0000000 --- a/test/instance/parse_test.jl +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG - -@testset "parse" begin - basedir = dirname(@__FILE__) - instance = RELOG.parsefile("$basedir/../../instances/s1.json") - - centers = instance.collection_centers - plants = instance.plants - products = instance.products - 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) - - @test length(centers) == 10 - @test centers[1].name == "C1" - @test centers[1].latitude == 7 - @test centers[1].latitude == 7 - @test centers[1].longitude == 7 - @test centers[1].amount == [934.56, 934.56] - @test centers[1].product.name == "P1" - - @test length(plants) == 6 - - plant = location_name_to_plant["L1"] - @test plant.plant_name == "F1" - @test plant.location_name == "L1" - @test plant.input.name == "P1" - @test plant.latitude == 0 - @test plant.longitude == 0 - - @test length(plant.sizes) == 2 - @test plant.sizes[1].capacity == 250 - @test plant.sizes[1].opening_cost == [500, 500] - @test plant.sizes[1].fixed_operating_cost == [30, 30] - @test plant.sizes[1].variable_operating_cost == [30, 30] - @test plant.sizes[2].capacity == 1000 - @test plant.sizes[2].opening_cost == [1250, 1250] - @test plant.sizes[2].fixed_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"] - @test p2.disposal_limit == [0.0, 0.0] - @test p2.disposal_cost == [0.0, 0.0] - - p3 = product_name_to_product["P3"] - @test length(plant.output) == 2 - @test plant.output[p2] == 0.2 - @test plant.output[p3] == 0.5 - @test plant.disposal_limit[p2] == [1, 1] - @test plant.disposal_limit[p3] == [1, 1] - @test plant.disposal_cost[p2] == [-10, -10] - @test plant.disposal_cost[p3] == [-10, -10] - - plant = location_name_to_plant["L3"] - @test plant.location_name == "L3" - @test plant.input.name == "P2" - @test plant.latitude == 25 - @test plant.longitude == 65 - - @test length(plant.sizes) == 2 - @test plant.sizes[1].capacity == 1000.0 - @test plant.sizes[1].opening_cost == [3000, 3000] - @test plant.sizes[1].fixed_operating_cost == [50, 50] - @test plant.sizes[1].variable_operating_cost == [50, 50] - @test plant.sizes[1] == plant.sizes[2] - - p4 = product_name_to_product["P4"] - @test plant.output[p3] == 0.05 - @test plant.output[p4] == 0.8 - @test plant.disposal_limit[p3] == [1e8, 1e8] - @test plant.disposal_limit[p4] == [0, 0] -end - -@testset "parse (geodb)" begin - basedir = dirname(@__FILE__) - instance = RELOG.parsefile("$basedir/../../instances/s2.json") - - centers = instance.collection_centers - @test centers[1].name == "C1" - @test centers[1].latitude == 41.83956 - @test centers[1].longitude == -88.08857 -end - -# @testset "parse (invalid)" begin -# basedir = dirname(@__FILE__) -# @test_throws ErrorException RELOG.parsefile("$basedir/../fixtures/s1-wrong-length.json") -# end diff --git a/test/model/build_test.jl b/test/model/build_test.jl deleted file mode 100644 index 5dc554a..0000000 --- a/test/model/build_test.jl +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats - -@testset "build" begin - basedir = dirname(@__FILE__) - instance = RELOG.parsefile("$basedir/../../instances/s1.json") - graph = RELOG.build_graph(instance) - model = RELOG.build_model(instance, graph, Cbc.Optimizer) - set_optimizer_attribute(model, "logLevel", 0) - - process_node_by_location_name = - Dict(n.location.location_name => n for n in graph.process_nodes) - - shipping_node_by_loc_and_prod_names = Dict( - (n.location.location_name, n.product.name) => n for n in graph.plant_shipping_nodes - ) - - @test length(model[:flow]) == 76 - @test length(model[:plant_dispose]) == 16 - @test length(model[:open_plant]) == 12 - @test length(model[:capacity]) == 12 - @test length(model[:expansion]) == 12 - - l1 = process_node_by_location_name["L1"] - v = model[:capacity][l1, 1] - @test lower_bound(v) == 0.0 - @test upper_bound(v) == 1000.0 - - v = model[:expansion][l1, 1] - @test lower_bound(v) == 0.0 - @test upper_bound(v) == 750.0 - - v = model[:plant_dispose][shipping_node_by_loc_and_prod_names["L1", "P2"], 1] - @test lower_bound(v) == 0.0 - @test upper_bound(v) == 1.0 -end diff --git a/test/model/resolve_test.jl b/test/model/resolve_test.jl deleted file mode 100644 index 9021000..0000000 --- a/test/model/resolve_test.jl +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG - -basedir = @__DIR__ - -@testset "Resolve" begin - # Shoud not crash - filename = "$basedir/../../instances/s1.json" - solution_old, model_old = RELOG.solve(filename, return_model = true) - solution_new = RELOG.resolve(model_old, filename) -end diff --git a/test/model/solve_test.jl b/test/model/solve_test.jl deleted file mode 100644 index 0f03bd4..0000000 --- a/test/model/solve_test.jl +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats - -basedir = dirname(@__FILE__) - -@testset "solve (exact)" begin - solution_filename_a = tempname() - solution_filename_b = tempname() - solution = RELOG.solve("$basedir/../../instances/s1.json", output = solution_filename_a) - - @test isfile(solution_filename_a) - - RELOG.write(solution, solution_filename_b) - @test isfile(solution_filename_b) - - @test "Costs" in keys(solution) - @test "Fixed operating (\$)" in keys(solution["Costs"]) - @test "Transportation (\$)" in keys(solution["Costs"]) - @test "Variable operating (\$)" in keys(solution["Costs"]) - @test "Total (\$)" in keys(solution["Costs"]) - - @test "Plants" in keys(solution) - @test "F1" in keys(solution["Plants"]) - @test "F2" in keys(solution["Plants"]) - @test "F3" 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 - -@testset "solve (heuristic)" begin - # Should not crash - solution = RELOG.solve("$basedir/../../instances/s1.json", heuristic = true) -end - -@testset "solve (infeasible)" begin - json = JSON.parsefile("$basedir/../../instances/s1.json") - for (location_name, location_dict) in json["products"]["P1"]["initial amounts"] - location_dict["amount (tonne)"] *= 1000 - end - @test_throws ErrorException("No solution available") RELOG.solve(RELOG.parse(json)) -end - -@testset "solve (with storage)" begin - basedir = dirname(@__FILE__) - filename = "$basedir/../fixtures/storage.json" - instance = RELOG.parsefile(filename) - @test instance.plants[1].storage_limit == 50.0 - @test instance.plants[1].storage_cost == [2.0, 1.5, 1.0] - - solution = RELOG.solve(filename) - plant_dict = solution["Plants"]["mega plant"]["Chicago"] - @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["Storage (tonne)"] == [50.0, 50.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"]["Storage (\$)"] == [100.0, 75.0, 0.0] - @test solution["Costs"]["Total (\$)"] == [600.0, 75.0, 100.0] -end diff --git a/test/reports_test.jl b/test/reports_test.jl deleted file mode 100644 index 9193977..0000000 --- a/test/reports_test.jl +++ /dev/null @@ -1,21 +0,0 @@ -# RELOG: Reverse Logistics Optimization -# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. -# Released under the modified BSD license. See COPYING.md for more details. - -using RELOG, JSON, GZip - -basedir = @__DIR__ - -@testset "Reports" begin - @testset "from solve" begin - solution = RELOG.solve("$basedir/../instances/s1.json") - tmp_filename = tempname() - # The following should not crash - RELOG.write_plant_emissions_report(solution, tmp_filename) - RELOG.write_plant_outputs_report(solution, tmp_filename) - RELOG.write_plants_report(solution, tmp_filename) - RELOG.write_products_report(solution, tmp_filename) - RELOG.write_transportation_emissions_report(solution, tmp_filename) - RELOG.write_transportation_report(solution, tmp_filename) - end -end diff --git a/test/runtests.jl b/test/runtests.jl deleted file mode 100644 index 1da75c7..0000000 --- a/test/runtests.jl +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2020 Argonne National Laboratory -# Written by Alinson Santos Xavier - -using Test - -@testset "RELOG" begin - @testset "Instance" begin - include("instance/compress_test.jl") - include("instance/geodb_test.jl") - include("instance/parse_test.jl") - end - @testset "Graph" begin - include("graph/build_test.jl") - include("graph/dist_test.jl") - end - @testset "Model" begin - include("model/build_test.jl") - include("model/solve_test.jl") - include("model/resolve_test.jl") - end - include("reports_test.jl") -end diff --git a/test/src/RELOGT.jl b/test/src/RELOGT.jl new file mode 100644 index 0000000..2691480 --- /dev/null +++ b/test/src/RELOGT.jl @@ -0,0 +1,51 @@ +module RELOGT + +using Test +using JuliaFormatter + +include("instance/compress_test.jl") +include("instance/geodb_test.jl") +include("instance/parse_test.jl") +include("graph/build_test.jl") +include("graph/dist_test.jl") +include("model/build_test.jl") +include("model/solve_test.jl") +include("model/resolve_test.jl") +include("reports_test.jl") + +basedir = dirname(@__FILE__) + +function fixture(path::String)::String + return "$basedir/../fixtures/$path" +end + +function runtests() + @testset "RELOG" begin + @testset "instance" begin + instance_compress_test() + instance_geodb_test() + instance_parse_test() + end + @testset "graph" begin + graph_build_test() + graph_dist_test() + end + @testset "model" begin + model_build_test() + model_solve_test() + model_resolve_test() + end + reports_test() + end + return +end + +function format() + JuliaFormatter.format(basedir, verbose = true) + JuliaFormatter.format("$basedir/../../src", verbose = true) + return +end + +export runtests, format + +end # module RELOGT diff --git a/test/src/graph/build_test.jl b/test/src/graph/build_test.jl new file mode 100644 index 0000000..070f899 --- /dev/null +++ b/test/src/graph/build_test.jl @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG + +function graph_build_test() + @testset "build_graph" begin + instance = RELOG.parsefile(fixture("s1.json")) + graph = RELOG.build_graph(instance) + process_node_by_location_name = + Dict(n.location.location_name => n for n in graph.process_nodes) + + @test length(graph.plant_shipping_nodes) == 8 + @test length(graph.collection_shipping_nodes) == 10 + @test length(graph.process_nodes) == 6 + + node = graph.collection_shipping_nodes[1] + @test node.location.name == "C1" + @test length(node.incoming_arcs) == 0 + @test length(node.outgoing_arcs) == 2 + @test node.outgoing_arcs[1].source.location.name == "C1" + @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].values["distance"] == 1695.364 + + node = process_node_by_location_name["L1"] + @test node.location.plant_name == "F1" + @test node.location.location_name == "L1" + @test length(node.incoming_arcs) == 10 + @test length(node.outgoing_arcs) == 2 + + node = process_node_by_location_name["L3"] + @test node.location.plant_name == "F2" + @test node.location.location_name == "L3" + @test length(node.incoming_arcs) == 2 + @test length(node.outgoing_arcs) == 2 + + @test length(graph.arcs) == 38 + end +end diff --git a/test/src/graph/dist_test.jl b/test/src/graph/dist_test.jl new file mode 100644 index 0000000..8dd084a --- /dev/null +++ b/test/src/graph/dist_test.jl @@ -0,0 +1,27 @@ +# RELOG: Reverse Logistics Optimization +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using RELOG + +function graph_dist_test() + @testset "KnnDrivingDistance" begin + # Euclidean distance between Chicago and Indianapolis + @test RELOG._calculate_distance( + 41.866, + -87.656, + 39.764, + -86.148, + RELOG.EuclideanDistance(), + ) == 265.818 + + # Approximate driving distance between Chicago and Indianapolis + @test RELOG._calculate_distance( + 41.866, + -87.656, + 39.764, + -86.148, + RELOG.KnnDrivingDistance(), + ) == 316.43 + end +end diff --git a/test/src/instance/compress_test.jl b/test/src/instance/compress_test.jl new file mode 100644 index 0000000..6652c63 --- /dev/null +++ b/test/src/instance/compress_test.jl @@ -0,0 +1,54 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG + +function instance_compress_test() + @testset "compress" begin + instance = RELOG.parsefile(fixture("s1.json")) + compressed = RELOG._compress(instance) + + product_name_to_product = Dict(p.name => p for p in compressed.products) + location_name_to_facility = Dict() + for p in compressed.plants + location_name_to_facility[p.location_name] = p + end + for c in compressed.collection_centers + location_name_to_facility[c.name] = c + end + + p1 = product_name_to_product["P1"] + p2 = product_name_to_product["P2"] + p3 = product_name_to_product["P3"] + c1 = location_name_to_facility["C1"] + l1 = location_name_to_facility["L1"] + + @test compressed.time == 1 + @test compressed.building_period == [1] + + @test p1.name == "P1" + @test p1.transportation_cost ≈ [0.015] + @test p1.transportation_energy ≈ [0.115] + @test p1.transportation_emissions["CO2"] ≈ [0.051] + @test p1.transportation_emissions["CH4"] ≈ [0.0025] + + @test c1.name == "C1" + @test c1.amount ≈ [1869.12] + + @test l1.plant_name == "F1" + @test l1.location_name == "L1" + @test l1.energy ≈ [0.115] + @test l1.emissions["CO2"] ≈ [0.051] + @test l1.emissions["CH4"] ≈ [0.0025] + @test l1.sizes[1].opening_cost ≈ [500] + @test l1.sizes[2].opening_cost ≈ [1250] + @test l1.sizes[1].fixed_operating_cost ≈ [60] + @test l1.sizes[2].fixed_operating_cost ≈ [60] + @test l1.sizes[1].variable_operating_cost ≈ [30] + @test l1.sizes[2].variable_operating_cost ≈ [30] + @test l1.disposal_limit[p2] ≈ [2.0] + @test l1.disposal_limit[p3] ≈ [2.0] + @test l1.disposal_cost[p2] ≈ [-10.0] + @test l1.disposal_cost[p3] ≈ [-10.0] + end +end diff --git a/test/src/instance/geodb_test.jl b/test/src/instance/geodb_test.jl new file mode 100644 index 0000000..f128ccc --- /dev/null +++ b/test/src/instance/geodb_test.jl @@ -0,0 +1,27 @@ +# RELOG: Reverse Logistics Optimization +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using RELOG + +function instance_geodb_test() + @testset "geodb_query (2018-us-county)" begin + region = RELOG.geodb_query("2018-us-county:17043") + @test region.centroid.lat == 41.83956 + @test region.centroid.lon == -88.08857 + @test region.population == 922_921 + end + + # @testset "geodb_query (2018-us-zcta)" begin + # region = RELOG.geodb_query("2018-us-zcta:60439") + # @test region.centroid.lat == 41.68241 + # @test region.centroid.lon == -87.98954 + # end + + @testset "geodb_query (us-state)" begin + region = RELOG.geodb_query("us-state:IL") + @test region.centroid.lat == 39.73939 + @test region.centroid.lon == -89.50414 + @test region.population == 12_671_821 + end +end diff --git a/test/src/instance/parse_test.jl b/test/src/instance/parse_test.jl new file mode 100644 index 0000000..16288e1 --- /dev/null +++ b/test/src/instance/parse_test.jl @@ -0,0 +1,87 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG + +function instance_parse_test() + @testset "parse" begin + instance = RELOG.parsefile(fixture("s1.json")) + + centers = instance.collection_centers + plants = instance.plants + products = instance.products + 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) + + @test length(centers) == 10 + @test centers[1].name == "C1" + @test centers[1].latitude == 7 + @test centers[1].latitude == 7 + @test centers[1].longitude == 7 + @test centers[1].amount == [934.56, 934.56] + @test centers[1].product.name == "P1" + + @test length(plants) == 6 + + plant = location_name_to_plant["L1"] + @test plant.plant_name == "F1" + @test plant.location_name == "L1" + @test plant.input.name == "P1" + @test plant.latitude == 0 + @test plant.longitude == 0 + + @test length(plant.sizes) == 2 + @test plant.sizes[1].capacity == 250 + @test plant.sizes[1].opening_cost == [500, 500] + @test plant.sizes[1].fixed_operating_cost == [30, 30] + @test plant.sizes[1].variable_operating_cost == [30, 30] + @test plant.sizes[2].capacity == 1000 + @test plant.sizes[2].opening_cost == [1250, 1250] + @test plant.sizes[2].fixed_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"] + @test p2.disposal_limit == [0.0, 0.0] + @test p2.disposal_cost == [0.0, 0.0] + + p3 = product_name_to_product["P3"] + @test length(plant.output) == 2 + @test plant.output[p2] == 0.2 + @test plant.output[p3] == 0.5 + @test plant.disposal_limit[p2] == [1, 1] + @test plant.disposal_limit[p3] == [1, 1] + @test plant.disposal_cost[p2] == [-10, -10] + @test plant.disposal_cost[p3] == [-10, -10] + + plant = location_name_to_plant["L3"] + @test plant.location_name == "L3" + @test plant.input.name == "P2" + @test plant.latitude == 25 + @test plant.longitude == 65 + + @test length(plant.sizes) == 2 + @test plant.sizes[1].capacity == 1000.0 + @test plant.sizes[1].opening_cost == [3000, 3000] + @test plant.sizes[1].fixed_operating_cost == [50, 50] + @test plant.sizes[1].variable_operating_cost == [50, 50] + @test plant.sizes[1] == plant.sizes[2] + + p4 = product_name_to_product["P4"] + @test plant.output[p3] == 0.05 + @test plant.output[p4] == 0.8 + @test plant.disposal_limit[p3] == [1e8, 1e8] + @test plant.disposal_limit[p4] == [0, 0] + end + + @testset "parse (geodb)" begin + instance = RELOG.parsefile(fixture("s2.json")) + centers = instance.collection_centers + @test centers[1].name == "C1" + @test centers[1].latitude == 41.83956 + @test centers[1].longitude == -88.08857 + end +end diff --git a/test/src/model/build_test.jl b/test/src/model/build_test.jl new file mode 100644 index 0000000..3c111ea --- /dev/null +++ b/test/src/model/build_test.jl @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats + +function model_build_test() + @testset "build" begin + instance = RELOG.parsefile(fixture("s1.json")) + graph = RELOG.build_graph(instance) + model = RELOG.build_model(instance, graph, Cbc.Optimizer) + set_optimizer_attribute(model, "logLevel", 0) + + process_node_by_location_name = + Dict(n.location.location_name => n for n in graph.process_nodes) + + shipping_node_by_loc_and_prod_names = Dict( + (n.location.location_name, n.product.name) => n for + n in graph.plant_shipping_nodes + ) + + @test length(model[:flow]) == 76 + @test length(model[:plant_dispose]) == 16 + @test length(model[:open_plant]) == 12 + @test length(model[:capacity]) == 12 + @test length(model[:expansion]) == 12 + + l1 = process_node_by_location_name["L1"] + v = model[:capacity][l1, 1] + @test lower_bound(v) == 0.0 + @test upper_bound(v) == 1000.0 + + v = model[:expansion][l1, 1] + @test lower_bound(v) == 0.0 + @test upper_bound(v) == 750.0 + + v = model[:plant_dispose][shipping_node_by_loc_and_prod_names["L1", "P2"], 1] + @test lower_bound(v) == 0.0 + @test upper_bound(v) == 1.0 + end +end diff --git a/test/src/model/resolve_test.jl b/test/src/model/resolve_test.jl new file mode 100644 index 0000000..32e3e23 --- /dev/null +++ b/test/src/model/resolve_test.jl @@ -0,0 +1,13 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG + +function model_resolve_test() + @testset "Resolve" begin + # Shoud not crash + filename = fixture("s1.json") + solution_old, model_old = RELOG.solve(filename, return_model = true) + solution_new = RELOG.resolve(model_old, filename) + end +end diff --git a/test/src/model/solve_test.jl b/test/src/model/solve_test.jl new file mode 100644 index 0000000..ecb948a --- /dev/null +++ b/test/src/model/solve_test.jl @@ -0,0 +1,70 @@ +# Copyright (C) 2020 Argonne National Laboratory +# Written by Alinson Santos Xavier + +using RELOG, Cbc, JuMP, Printf, JSON, MathOptInterface.FileFormats + + +function model_solve_test() + @testset "solve (exact)" begin + solution_filename_a = tempname() + solution_filename_b = tempname() + solution = RELOG.solve(fixture("s1.json"), output = solution_filename_a) + + @test isfile(solution_filename_a) + + RELOG.write(solution, solution_filename_b) + @test isfile(solution_filename_b) + + @test "Costs" in keys(solution) + @test "Fixed operating (\$)" in keys(solution["Costs"]) + @test "Transportation (\$)" in keys(solution["Costs"]) + @test "Variable operating (\$)" in keys(solution["Costs"]) + @test "Total (\$)" in keys(solution["Costs"]) + + @test "Plants" in keys(solution) + @test "F1" in keys(solution["Plants"]) + @test "F2" in keys(solution["Plants"]) + @test "F3" 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 + + @testset "solve (heuristic)" begin + # Should not crash + solution = RELOG.solve(fixture("s1.json"), heuristic = true) + end + + @testset "solve (infeasible)" begin + json = JSON.parsefile(fixture("s1.json")) + for (location_name, location_dict) in json["products"]["P1"]["initial amounts"] + location_dict["amount (tonne)"] *= 1000 + end + @test_throws ErrorException("No solution available") RELOG.solve(RELOG.parse(json)) + end + + @testset "solve (with storage)" begin + filename = fixture("storage.json") + instance = RELOG.parsefile(filename) + @test instance.plants[1].storage_limit == 50.0 + @test instance.plants[1].storage_cost == [2.0, 1.5, 1.0] + + solution = RELOG.solve(filename) + plant_dict = solution["Plants"]["mega plant"]["Chicago"] + @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["Storage (tonne)"] == [50.0, 50.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"]["Storage (\$)"] == [100.0, 75.0, 0.0] + @test solution["Costs"]["Total (\$)"] == [600.0, 75.0, 100.0] + end +end diff --git a/test/src/reports_test.jl b/test/src/reports_test.jl new file mode 100644 index 0000000..3f3971b --- /dev/null +++ b/test/src/reports_test.jl @@ -0,0 +1,21 @@ +# RELOG: Reverse Logistics Optimization +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using RELOG, JSON, GZip + +function reports_test() + @testset "reports" begin + @testset "from solve" begin + solution = RELOG.solve(fixture("s1.json")) + tmp_filename = tempname() + # The following should not crash + RELOG.write_plant_emissions_report(solution, tmp_filename) + RELOG.write_plant_outputs_report(solution, tmp_filename) + RELOG.write_plants_report(solution, tmp_filename) + RELOG.write_products_report(solution, tmp_filename) + RELOG.write_transportation_emissions_report(solution, tmp_filename) + RELOG.write_transportation_report(solution, tmp_filename) + end + end +end From e86ae0f81837534069aaf7fa8fcb102d397fc649 Mon Sep 17 00:00:00 2001 From: "Alinson S. Xavier" Date: Wed, 15 Feb 2023 13:22:47 -0600 Subject: [PATCH 12/12] Add RELOG.version() --- Project.toml | 3 ++- src/RELOG.jl | 5 +++++ src/model/solve.jl | 10 +++++----- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index baacbfd..f747367 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" ProgressBars = "49802e3a-d2f1-5c88-81d8-b72133a6f568" Shapefile = "8e980c4a-a4fe-5da2-b3a7-4b4b0353a2f4" @@ -40,9 +41,9 @@ JSON = "0.21" JSONSchema = "1" JuMP = "1" MathOptInterface = "1" +NearestNeighbors = "0.4" OrderedCollections = "1" ProgressBars = "1" Shapefile = "0.8" ZipFile = "0.10" julia = "1" -NearestNeighbors = "0.4" diff --git a/src/RELOG.jl b/src/RELOG.jl index 0f39e32..7d553b7 100644 --- a/src/RELOG.jl +++ b/src/RELOG.jl @@ -4,6 +4,10 @@ module RELOG +using Pkg + +version() = Pkg.dependencies()[Base.UUID("a2afcdf7-cf04-4913-85f9-c0d81ddf2008")].version + include("instance/structs.jl") include("graph/structs.jl") @@ -25,4 +29,5 @@ include("reports/products.jl") include("reports/tr_emissions.jl") include("reports/tr.jl") include("reports/write.jl") + end diff --git a/src/model/solve.jl b/src/model/solve.jl index d517c12..1adfc46 100644 --- a/src/model/solve.jl +++ b/src/model/solve.jl @@ -14,14 +14,14 @@ end function _print_graph_stats(instance::Instance, graph::Graph)::Nothing - @info @sprintf(" %12d time periods", instance.time) - @info @sprintf(" %12d process nodes", length(graph.process_nodes)) - @info @sprintf(" %12d shipping nodes (plant)", length(graph.plant_shipping_nodes)) + @info @sprintf("%12d time periods", instance.time) + @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 (collection)", + "%12d shipping nodes (collection)", length(graph.collection_shipping_nodes) ) - @info @sprintf(" %12d arcs", length(graph.arcs)) + @info @sprintf("%12d arcs", length(graph.arcs)) return end