mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-06 07:48:50 -06:00
Implement first version of multi-period simulations
This commit is contained in:
@@ -17,7 +17,7 @@ end
|
||||
|
||||
mutable struct ProcessNode <: Node
|
||||
index::Int
|
||||
plant::Plant
|
||||
location::Plant
|
||||
incoming_arcs::Array{Arc}
|
||||
outgoing_arcs::Array{Arc}
|
||||
end
|
||||
@@ -79,8 +79,8 @@ function build_graph(instance::Instance)::Graph
|
||||
for dest in process_nodes_by_input_product[source.product]
|
||||
distance = calculate_distance(source.location.latitude,
|
||||
source.location.longitude,
|
||||
dest.plant.latitude,
|
||||
dest.plant.longitude)
|
||||
dest.location.latitude,
|
||||
dest.location.longitude)
|
||||
values = Dict("distance" => distance)
|
||||
arc = Arc(source, dest, values)
|
||||
push!(source.outgoing_arcs, arc)
|
||||
@@ -91,7 +91,7 @@ function build_graph(instance::Instance)::Graph
|
||||
|
||||
# Build arcs from process nodes to shipping nodes within a plant
|
||||
for source in process_nodes
|
||||
plant = source.plant
|
||||
plant = source.location
|
||||
for dest in shipping_nodes_by_plant[plant]
|
||||
weight = plant.output[dest.product]
|
||||
values = Dict("weight" => weight)
|
||||
|
||||
@@ -6,38 +6,41 @@ using JSON, JSONSchema
|
||||
|
||||
mutable struct Product
|
||||
name::String
|
||||
transportation_cost::Float64
|
||||
transportation_cost::Array{Float64}
|
||||
end
|
||||
|
||||
|
||||
mutable struct CollectionCenter
|
||||
index::Int64
|
||||
name::String
|
||||
latitude::Float64
|
||||
longitude::Float64
|
||||
product::Product
|
||||
amount::Float64
|
||||
amount::Array{Float64}
|
||||
end
|
||||
|
||||
|
||||
mutable struct Plant
|
||||
index::Int64
|
||||
plant_name::String
|
||||
location_name::String
|
||||
input::Product
|
||||
output::Dict{Product, Float64}
|
||||
latitude::Float64
|
||||
longitude::Float64
|
||||
variable_operating_cost::Float64
|
||||
fixed_operating_cost::Float64
|
||||
opening_cost::Float64
|
||||
variable_operating_cost::Array{Float64}
|
||||
fixed_operating_cost::Array{Float64}
|
||||
opening_cost::Array{Float64}
|
||||
base_capacity::Float64
|
||||
max_capacity::Float64
|
||||
expansion_cost::Float64
|
||||
disposal_limit::Dict{Product, Float64}
|
||||
disposal_cost::Dict{Product, Float64}
|
||||
expansion_cost::Array{Float64}
|
||||
disposal_limit::Dict{Product, Array{Float64}}
|
||||
disposal_cost::Dict{Product, Array{Float64}}
|
||||
end
|
||||
|
||||
|
||||
mutable struct Instance
|
||||
time::Int64
|
||||
products::Array{Product, 1}
|
||||
collection_centers::Array{CollectionCenter, 1}
|
||||
plants::Array{Plant, 1}
|
||||
@@ -49,16 +52,21 @@ function load(path::String)::Instance
|
||||
json = JSON.parsefile(path)
|
||||
schema = Schema(JSON.parsefile("$basedir/schemas/input.json"))
|
||||
|
||||
validation_results = JSONSchema.validate(json, schema)
|
||||
if validation_results !== nothing
|
||||
println(validation_results)
|
||||
throw("Invalid input file")
|
||||
result = JSONSchema.validate(json, schema)
|
||||
if result !== nothing
|
||||
if result isa JSONSchema.SingleIssue
|
||||
path = join(result.path, " → ")
|
||||
msg = "$(result.x) $(result.msg) in $(path)"
|
||||
else
|
||||
msg = convert(String, result)
|
||||
end
|
||||
throw(msg)
|
||||
end
|
||||
|
||||
T = json["parameters"]["time periods"]
|
||||
plants = Plant[]
|
||||
products = Product[]
|
||||
collection_centers = CollectionCenter[]
|
||||
plants = Plant[]
|
||||
|
||||
product_name_to_product = Dict{String, Product}()
|
||||
|
||||
# Create products
|
||||
@@ -70,7 +78,8 @@ function load(path::String)::Instance
|
||||
# Create collection centers
|
||||
if "initial amounts" in keys(product_dict)
|
||||
for (center_name, center_dict) in product_dict["initial amounts"]
|
||||
center = CollectionCenter(center_name,
|
||||
center = CollectionCenter(length(collection_centers) + 1,
|
||||
center_name,
|
||||
center_dict["latitude"],
|
||||
center_dict["longitude"],
|
||||
product,
|
||||
@@ -93,8 +102,8 @@ function load(path::String)::Instance
|
||||
end
|
||||
|
||||
for (location_name, location_dict) in plant_dict["locations"]
|
||||
disposal_limit = Dict(p => 0.0 for p in keys(output))
|
||||
disposal_cost = Dict(p => 0.0 for p in keys(output))
|
||||
disposal_limit = Dict(p => [0.0 for t in 1:T] for p in keys(output))
|
||||
disposal_cost = Dict(p => [0.0 for t in 1:T] for p in keys(output))
|
||||
|
||||
# Plant disposal
|
||||
if "disposal" in keys(location_dict)
|
||||
@@ -106,7 +115,7 @@ function load(path::String)::Instance
|
||||
|
||||
base_capacity = 1e8
|
||||
max_capacity = 1e8
|
||||
expansion_cost = 0
|
||||
expansion_cost = [0.0 for t in 1:T]
|
||||
|
||||
if "base capacity" in keys(location_dict)
|
||||
base_capacity = location_dict["base capacity"]
|
||||
@@ -120,7 +129,8 @@ function load(path::String)::Instance
|
||||
expansion_cost = location_dict["expansion cost"]
|
||||
end
|
||||
|
||||
plant = Plant(plant_name,
|
||||
plant = Plant(length(plants) + 1,
|
||||
plant_name,
|
||||
location_name,
|
||||
input,
|
||||
output,
|
||||
@@ -138,5 +148,5 @@ function load(path::String)::Instance
|
||||
end
|
||||
end
|
||||
|
||||
return Instance(products, collection_centers, plants)
|
||||
return Instance(T, products, collection_centers, plants)
|
||||
end
|
||||
|
||||
255
src/model.jl
255
src/model.jl
@@ -23,60 +23,76 @@ end
|
||||
|
||||
|
||||
function create_vars!(model::ManufacturingModel)
|
||||
mip, vars, graph = model.mip, model.vars, model.graph
|
||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
||||
|
||||
vars.flow = Dict(a => @variable(mip, lower_bound=0)
|
||||
for a in graph.arcs)
|
||||
vars.flow = Dict((a, t) => @variable(mip,
|
||||
lower_bound=0,
|
||||
base_name="flow($(a.source.location.index),$(a.dest.location.index),$t)")
|
||||
for a in graph.arcs, t in 1:T)
|
||||
|
||||
vars.dispose = Dict(n => @variable(mip,
|
||||
lower_bound = 0,
|
||||
upper_bound = n.location.disposal_limit[n.product])
|
||||
for n in values(graph.plant_shipping_nodes))
|
||||
vars.dispose = Dict((n, t) => @variable(mip,
|
||||
lower_bound=0,
|
||||
upper_bound=n.location.disposal_limit[n.product][t],
|
||||
base_name="dispose($(n.location.index),$(n.product.name),$t)")
|
||||
for n in values(graph.plant_shipping_nodes), t in 1:T)
|
||||
|
||||
vars.open_plant = Dict(n => @variable(mip, binary=true)
|
||||
for n in values(graph.process_nodes))
|
||||
vars.open_plant = Dict((n, t) => @variable(mip,
|
||||
binary=true,
|
||||
base_name="open_plant($(n.location.index),$t)")
|
||||
for n in values(graph.process_nodes), t in 1:T)
|
||||
|
||||
vars.is_open = Dict((n, t) => @variable(mip,
|
||||
binary=true,
|
||||
base_name="is_open($(n.location.index),$t)")
|
||||
for n in values(graph.process_nodes), t in 1:T)
|
||||
|
||||
vars.capacity = Dict((n, t) => @variable(mip,
|
||||
lower_bound = 0,
|
||||
upper_bound = n.location.max_capacity,
|
||||
base_name="capacity($(n.location.index),$t)")
|
||||
for n in values(graph.process_nodes), t in 1:T)
|
||||
|
||||
vars.capacity = Dict(n => @variable(mip,
|
||||
lower_bound = 0,
|
||||
upper_bound = n.plant.max_capacity)
|
||||
for n in values(graph.process_nodes))
|
||||
|
||||
vars.expansion = Dict(n => @variable(mip,
|
||||
lower_bound = 0,
|
||||
upper_bound = (n.plant.max_capacity - n.plant.base_capacity))
|
||||
for n in values(graph.process_nodes))
|
||||
vars.expansion = Dict((n, t) => @variable(mip,
|
||||
lower_bound = 0,
|
||||
upper_bound = (n.location.max_capacity - n.location.base_capacity),
|
||||
base_name="expansion($(n.location.index),$t)")
|
||||
for n in values(graph.process_nodes), t in 1:T)
|
||||
end
|
||||
|
||||
|
||||
function create_objective_function!(model::ManufacturingModel)
|
||||
mip, vars, graph = model.mip, model.vars, model.graph
|
||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
||||
obj = @expression(mip, 0 * @variable(mip))
|
||||
|
||||
# Process node costs
|
||||
for n in values(graph.process_nodes)
|
||||
for n in values(graph.process_nodes), t in 1:T
|
||||
|
||||
# Transportation and variable operating costs
|
||||
for a in n.incoming_arcs
|
||||
c = n.plant.input.transportation_cost * a.values["distance"]
|
||||
c += n.plant.variable_operating_cost
|
||||
add_to_expression!(obj, c, vars.flow[a])
|
||||
c = n.location.input.transportation_cost[t] * a.values["distance"]
|
||||
c += n.location.variable_operating_cost[t]
|
||||
add_to_expression!(obj, c, vars.flow[a, t])
|
||||
end
|
||||
|
||||
# Fixed and opening costss
|
||||
add_to_expression!(obj,
|
||||
n.plant.fixed_operating_cost + n.plant.opening_cost,
|
||||
vars.open_plant[n])
|
||||
# Opening costs
|
||||
add_to_expression!(obj, n.location.opening_cost[t], vars.open_plant[n, t])
|
||||
|
||||
# Fixed operating costss
|
||||
add_to_expression!(obj, n.location.fixed_operating_cost[t], vars.is_open[n, t])
|
||||
|
||||
# Expansion costs
|
||||
add_to_expression!(obj, n.plant.expansion_cost,
|
||||
vars.expansion[n])
|
||||
if t < T
|
||||
add_to_expression!(obj,
|
||||
n.location.expansion_cost[t] - n.location.expansion_cost[t + 1],
|
||||
vars.expansion[n, t])
|
||||
else
|
||||
add_to_expression!(obj, n.location.expansion_cost[t], vars.expansion[n, t])
|
||||
end
|
||||
end
|
||||
|
||||
# Disposal costs
|
||||
for n in values(graph.plant_shipping_nodes)
|
||||
add_to_expression!(obj,
|
||||
n.location.disposal_cost[n.product],
|
||||
vars.dispose[n])
|
||||
for n in values(graph.plant_shipping_nodes), t in 1:T
|
||||
add_to_expression!(obj, n.location.disposal_cost[n.product][t], vars.dispose[n, t])
|
||||
end
|
||||
|
||||
@objective(mip, Min, obj)
|
||||
@@ -84,41 +100,56 @@ end
|
||||
|
||||
|
||||
function create_shipping_node_constraints!(model::ManufacturingModel)
|
||||
mip, vars, graph = model.mip, model.vars, model.graph
|
||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
||||
|
||||
# Collection centers
|
||||
for n in graph.collection_shipping_nodes
|
||||
@constraint(mip, sum(vars.flow[a] for a in n.outgoing_arcs) == n.location.amount)
|
||||
end
|
||||
|
||||
# Plants
|
||||
for n in graph.plant_shipping_nodes
|
||||
@constraint(mip,
|
||||
sum(vars.flow[a] for a in n.incoming_arcs) ==
|
||||
sum(vars.flow[a] for a in n.outgoing_arcs) + vars.dispose[n])
|
||||
for t in 1:T
|
||||
# Collection centers
|
||||
for n in graph.collection_shipping_nodes
|
||||
@constraint(mip, sum(vars.flow[a, t] for a in n.outgoing_arcs) == n.location.amount[t])
|
||||
end
|
||||
|
||||
# Plants
|
||||
for n in graph.plant_shipping_nodes
|
||||
@constraint(mip,
|
||||
sum(vars.flow[a, t] for a in n.incoming_arcs) ==
|
||||
sum(vars.flow[a, t] for a in n.outgoing_arcs) + vars.dispose[n, t])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function create_process_node_constraints!(model::ManufacturingModel)
|
||||
mip, vars, graph = model.mip, model.vars, model.graph
|
||||
mip, vars, graph, T = model.mip, model.vars, model.graph, model.instance.time
|
||||
|
||||
for n in graph.process_nodes
|
||||
|
||||
for n in graph.process_nodes, t in 1:T
|
||||
# Output amount is implied by input amount
|
||||
input_sum = isempty(n.incoming_arcs) ? 0 : sum(vars.flow[a] for a in n.incoming_arcs)
|
||||
input_sum = isempty(n.incoming_arcs) ? 0 : sum(vars.flow[a, t] for a in n.incoming_arcs)
|
||||
for a in n.outgoing_arcs
|
||||
@constraint(mip, vars.flow[a] == a.values["weight"] * input_sum)
|
||||
@constraint(mip, vars.flow[a, t] == a.values["weight"] * input_sum)
|
||||
end
|
||||
|
||||
# If plant is closed, capacity is zero
|
||||
@constraint(mip, vars.capacity[n] <= n.plant.max_capacity * vars.open_plant[n])
|
||||
@constraint(mip, vars.capacity[n, t] <= n.location.max_capacity * vars.is_open[n, t])
|
||||
|
||||
# Capacity is linked to expansion
|
||||
@constraint(mip, vars.capacity[n] <= n.plant.base_capacity + vars.expansion[n])
|
||||
@constraint(mip, vars.capacity[n, t] <= n.location.base_capacity + vars.expansion[n, t])
|
||||
|
||||
# Input sum must be smaller than capacity
|
||||
@constraint(mip, input_sum <= vars.capacity[n])
|
||||
@constraint(mip, input_sum <= vars.capacity[n, t])
|
||||
|
||||
if t > 1
|
||||
# Plant capacity can only increase over time
|
||||
@constraint(mip, vars.capacity[n, t] >= vars.capacity[n, t-1])
|
||||
@constraint(mip, vars.expansion[n, t] >= vars.expansion[n, t-1])
|
||||
end
|
||||
|
||||
# Plant is currently open if it was already open in the previous time period or
|
||||
# if it was built just now
|
||||
if t > 1
|
||||
@constraint(mip, vars.is_open[n, t] == vars.is_open[n, t-1] + vars.open_plant[n, t])
|
||||
else
|
||||
@constraint(mip, vars.is_open[n, t] == vars.open_plant[n, t])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -141,19 +172,22 @@ end
|
||||
|
||||
function get_solution(model::ManufacturingModel)
|
||||
mip, vars, graph, instance = model.mip, model.vars, model.graph, model.instance
|
||||
T = instance.time
|
||||
|
||||
output = Dict(
|
||||
"plants" => Dict(),
|
||||
"costs" => Dict(
|
||||
"fixed" => 0.0,
|
||||
"variable" => 0.0,
|
||||
"transportation" => 0.0,
|
||||
"disposal" => 0.0,
|
||||
"total" => 0.0,
|
||||
"expansion" => 0.0,
|
||||
"fixed operating" => zeros(T),
|
||||
"variable operating" => zeros(T),
|
||||
"opening" => zeros(T),
|
||||
"transportation" => zeros(T),
|
||||
"disposal" => zeros(T),
|
||||
"expansion" => zeros(T),
|
||||
"total" => zeros(T),
|
||||
)
|
||||
)
|
||||
|
||||
plant_to_process_node = Dict(n.plant => n for n in graph.process_nodes)
|
||||
plant_to_process_node = Dict(n.location => n for n in graph.process_nodes)
|
||||
plant_to_shipping_nodes = Dict()
|
||||
for p in instance.plants
|
||||
plant_to_shipping_nodes[p] = []
|
||||
@@ -163,7 +197,7 @@ function get_solution(model::ManufacturingModel)
|
||||
end
|
||||
|
||||
for plant in instance.plants
|
||||
skip_plant = true
|
||||
skip_plant = false
|
||||
process_node = plant_to_process_node[plant]
|
||||
plant_dict = Dict{Any, Any}(
|
||||
"input" => Dict(),
|
||||
@@ -171,31 +205,44 @@ function get_solution(model::ManufacturingModel)
|
||||
"send" => Dict(),
|
||||
"dispose" => Dict(),
|
||||
),
|
||||
"total input" => 0.0,
|
||||
"total input" => [0.0 for t in 1:T],
|
||||
"total output" => Dict(),
|
||||
"latitude" => plant.latitude,
|
||||
"longitude" => plant.longitude,
|
||||
"capacity" => JuMP.value(vars.capacity[process_node]),
|
||||
"fixed cost" => JuMP.value(vars.open_plant[process_node]) * (plant.opening_cost + plant.fixed_operating_cost),
|
||||
"expansion cost" => JuMP.value(vars.expansion[process_node]) * plant.expansion_cost,
|
||||
"capacity" => [JuMP.value(vars.capacity[process_node, t])
|
||||
for t in 1:T],
|
||||
"opening cost" => [JuMP.value(vars.open_plant[process_node, t]) * plant.opening_cost[t]
|
||||
for t in 1:T],
|
||||
"fixed operating cost" => [JuMP.value(vars.is_open[process_node, t]) * plant.fixed_operating_cost[t]
|
||||
for t in 1:T],
|
||||
"expansion cost" => [plant.expansion_cost[t] *
|
||||
(if t > 1
|
||||
JuMP.value(vars.expansion[process_node, t]) - JuMP.value(vars.expansion[process_node, t-1])
|
||||
else
|
||||
JuMP.value(vars.expansion[process_node, t])
|
||||
end)
|
||||
for t in 1:T],
|
||||
)
|
||||
output["costs"]["fixed"] += plant_dict["fixed cost"]
|
||||
output["costs"]["fixed operating"] += plant_dict["fixed operating cost"]
|
||||
output["costs"]["opening"] += plant_dict["opening cost"]
|
||||
output["costs"]["expansion"] += plant_dict["expansion cost"]
|
||||
|
||||
# Inputs
|
||||
for a in process_node.incoming_arcs
|
||||
val = JuMP.value(vars.flow[a])
|
||||
if val <= 1e-3
|
||||
vals = [JuMP.value(vars.flow[a, t]) for t in 1:T]
|
||||
if sum(vals) <= 1e-3
|
||||
continue
|
||||
end
|
||||
skip_plant = false
|
||||
dict = Dict{Any, Any}(
|
||||
"amount" => val,
|
||||
"amount" => vals,
|
||||
"distance" => a.values["distance"],
|
||||
"latitude" => a.source.location.latitude,
|
||||
"longitude" => a.source.location.longitude,
|
||||
"transportation cost" => a.source.product.transportation_cost * val,
|
||||
"variable operating cost" => plant.variable_operating_cost * val,
|
||||
"transportation cost" => [a.source.product.transportation_cost[t] * vals[t]
|
||||
for t in 1:T],
|
||||
"variable operating cost" => [plant.variable_operating_cost[t] * vals[t]
|
||||
for t in 1:T],
|
||||
)
|
||||
if a.source.location isa CollectionCenter
|
||||
plant_name = "Origin"
|
||||
@@ -209,45 +256,45 @@ function get_solution(model::ManufacturingModel)
|
||||
plant_dict["input"][plant_name] = Dict()
|
||||
end
|
||||
plant_dict["input"][plant_name][location_name] = dict
|
||||
plant_dict["total input"] += val
|
||||
plant_dict["total input"] += vals
|
||||
output["costs"]["transportation"] += dict["transportation cost"]
|
||||
output["costs"]["variable"] += dict["variable operating cost"]
|
||||
output["costs"]["variable operating"] += dict["variable operating cost"]
|
||||
end
|
||||
|
||||
# Outputs
|
||||
for shipping_node in plant_to_shipping_nodes[plant]
|
||||
product_name = shipping_node.product.name
|
||||
plant_dict["total output"][product_name] = 0.0
|
||||
plant_dict["output"]["send"][product_name] = product_dict = Dict()
|
||||
# # Outputs
|
||||
# for shipping_node in plant_to_shipping_nodes[plant]
|
||||
# product_name = shipping_node.product.name
|
||||
# plant_dict["total output"][product_name] = 0.0
|
||||
# plant_dict["output"]["send"][product_name] = product_dict = Dict()
|
||||
|
||||
disposal_amount = JuMP.value(vars.dispose[shipping_node])
|
||||
if disposal_amount > 1e-5
|
||||
plant_dict["output"]["dispose"][product_name] = disposal_dict = Dict()
|
||||
disposal_dict["amount"] = JuMP.value(model.vars.dispose[shipping_node])
|
||||
disposal_dict["cost"] = disposal_dict["amount"] * plant.disposal_cost[shipping_node.product]
|
||||
plant_dict["total output"][product_name] += disposal_amount
|
||||
output["costs"]["disposal"] += disposal_dict["cost"]
|
||||
end
|
||||
# disposal_amount = JuMP.value(vars.dispose[shipping_node])
|
||||
# if disposal_amount > 1e-5
|
||||
# plant_dict["output"]["dispose"][product_name] = disposal_dict = Dict()
|
||||
# disposal_dict["amount"] = JuMP.value(model.vars.dispose[shipping_node])
|
||||
# disposal_dict["cost"] = disposal_dict["amount"] * plant.disposal_cost[shipping_node.product]
|
||||
# plant_dict["total output"][product_name] += disposal_amount
|
||||
# output["costs"]["disposal"] += disposal_dict["cost"]
|
||||
# end
|
||||
|
||||
for a in shipping_node.outgoing_arcs
|
||||
val = JuMP.value(vars.flow[a])
|
||||
if val <= 1e-3
|
||||
continue
|
||||
end
|
||||
skip_plant = false
|
||||
dict = Dict(
|
||||
"amount" => val,
|
||||
"distance" => a.values["distance"],
|
||||
"latitude" => a.dest.plant.latitude,
|
||||
"longitude" => a.dest.plant.longitude,
|
||||
)
|
||||
if a.dest.plant.plant_name ∉ keys(product_dict)
|
||||
product_dict[a.dest.plant.plant_name] = Dict()
|
||||
end
|
||||
product_dict[a.dest.plant.plant_name][a.dest.plant.location_name] = dict
|
||||
plant_dict["total output"][product_name] += val
|
||||
end
|
||||
end
|
||||
# for a in shipping_node.outgoing_arcs
|
||||
# val = JuMP.value(vars.flow[a])
|
||||
# if val <= 1e-3
|
||||
# continue
|
||||
# end
|
||||
# skip_plant = false
|
||||
# dict = Dict(
|
||||
# "amount" => val,
|
||||
# "distance" => a.values["distance"],
|
||||
# "latitude" => a.dest.location.latitude,
|
||||
# "longitude" => a.dest.location.longitude,
|
||||
# )
|
||||
# if a.dest.location.plant_name ∉ keys(product_dict)
|
||||
# product_dict[a.dest.location.plant_name] = Dict()
|
||||
# end
|
||||
# product_dict[a.dest.location.plant_name][a.dest.location.location_name] = dict
|
||||
# plant_dict["total output"][product_name] += val
|
||||
# end
|
||||
# end
|
||||
|
||||
if !skip_plant
|
||||
if plant.plant_name ∉ keys(output["plants"])
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
"$id": "https://axavier.org/ReverseManufacturing/input/schema",
|
||||
"title": "Schema for ReverseManufacturing Input File",
|
||||
"definitions": {
|
||||
"TimeSeries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"Parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"time": { "type": "number" }
|
||||
},
|
||||
"required": [
|
||||
"time periods"
|
||||
]
|
||||
},
|
||||
"Plant": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
@@ -28,19 +43,19 @@
|
||||
"properties": {
|
||||
"latitude": { "type": "number" },
|
||||
"longitude": { "type": "number" },
|
||||
"variable operating cost": { "type": "number" },
|
||||
"fixed operating cost": { "type": "number" },
|
||||
"opening cost": { "type": "number" },
|
||||
"variable operating cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"fixed operating cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"opening cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"base capacity": { "type": "number" },
|
||||
"max capacity": { "type": "number" },
|
||||
"expansion cost": { "type": "number" },
|
||||
"expansion cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"disposal": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cost": { "type": "number" },
|
||||
"limit": { "type": "number" }
|
||||
"cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"limit": { "$ref": "#/definitions/TimeSeries" }
|
||||
},
|
||||
"required": [
|
||||
"cost"
|
||||
@@ -64,7 +79,7 @@
|
||||
"properties": {
|
||||
"latitude": { "type": "number" },
|
||||
"longitude": { "type": "number" },
|
||||
"amount": { "type": "number" }
|
||||
"amount": { "$ref": "#/definitions/TimeSeries" }
|
||||
},
|
||||
"required": [
|
||||
"latitude",
|
||||
@@ -78,7 +93,7 @@
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"transportation cost": { "type": "number" },
|
||||
"transportation cost": { "$ref": "#/definitions/TimeSeries" },
|
||||
"initial amounts": { "$ref": "#/definitions/InitialAmount" }
|
||||
},
|
||||
"required": [
|
||||
@@ -89,6 +104,7 @@
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parameters": { "$ref": "#/definitions/Parameters" },
|
||||
"plants": { "$ref": "#/definitions/Plant" },
|
||||
"products": { "$ref": "#/definitions/Product" }
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user