-
Notifications
You must be signed in to change notification settings - Fork 4
Enhance/transfer energy potentials #81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2f62de0
8524f62
2d76031
01cdf0c
7a82100
08b8f5d
ad926cd
5540458
49c7fa5
f494f9c
9bff364
b77124e
81fc9b0
11263a2
0692c5c
9f58fc9
4cffb4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -60,7 +60,7 @@ function create_model( | |
| # Declaration of element variables and constraints of the problem | ||
| for 𝒳 ∈ 𝒳ᵛᵉᶜ | ||
| variables_capacity(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒯, modeltype) | ||
| variables_flow(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒯, modeltype) | ||
| variables_flow(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change is a breaking change. I am not opposed to it as I do not really see another approach, but I think it is beneficial to think whether we want to include additional breaking changes. The breaking change could be circumvented by adding the following default method: variables_flow(m, 𝒳::Vector{<:AbstractElement}, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype::EnergyModel) =
variables_flow(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒯, modeltype)I tested it with |
||
| variables_opex(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒯, modeltype) | ||
| variables_capex(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒯, modeltype) | ||
| variables_emission(m, 𝒳, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype) | ||
|
|
@@ -230,7 +230,7 @@ By default, all nodes `𝒩` and links `ℒ` only allow for unidirectional flow. | |
| bidirectional flow through providing a method to the function [`is_unidirectional`](@ref) | ||
| for new link/node types. | ||
| """ | ||
| function variables_flow(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel) | ||
| function variables_flow(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype::EnergyModel) | ||
| # Extract the nodes with inputs and outputs | ||
| 𝒩ⁱⁿ = filter(has_input, 𝒩) | ||
| 𝒩ᵒᵘᵗ = filter(has_output, 𝒩) | ||
|
|
@@ -249,8 +249,14 @@ function variables_flow(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype: | |
| for n_out ∈ 𝒩ᵒᵘᵗ⁻ᵘⁿⁱ, t ∈ 𝒯, p ∈ outputs(n_out) | ||
| set_lower_bound(m[:flow_out][n_out, t, p], 0) | ||
| end | ||
|
|
||
| # Create new flow variables for specific resource types | ||
| for p_sub in res_types_seg(𝒫) | ||
| variables_flow_resource(m, 𝒩, p_sub, 𝒯, modeltype) | ||
| end | ||
|
|
||
| end | ||
| function variables_flow(m, ℒ::Vector{<:Link}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel) | ||
| function variables_flow(m, ℒ::Vector{<:Link}, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype::EnergyModel) | ||
| # Create the link flow variables | ||
| @variable(m, link_in[l ∈ ℒ, 𝒯, inputs(l)]) | ||
| @variable(m, link_out[l ∈ ℒ, 𝒯, outputs(l)]) | ||
|
|
@@ -266,8 +272,29 @@ function variables_flow(m, ℒ::Vector{<:Link}, 𝒳ᵛᵉᶜ, 𝒯, modeltype:: | |
| set_lower_bound(m[:link_out][l, t, p], 0) | ||
| end | ||
| end | ||
|
|
||
| # Create new flow variables for specific resource types | ||
| for p_sub in res_types_seg(𝒫) | ||
| variables_flow_resource(m, ℒ, p_sub, 𝒯, modeltype) | ||
| end | ||
| end | ||
|
|
||
| """ | ||
| variables_flow_resource(m, ℒ::Vector{<:Link}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) | ||
| variables_flow_resource(m, 𝒩::Vector{<:Node}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) | ||
|
|
||
| Create resource-specific flow variables for links or nodes. | ||
|
|
||
| This function is called from [`variables_flow`](@ref) for each subset of resources | ||
| sharing the same type. It can be used to add variables and bounds for specialized | ||
| resource classes while keeping the default flow variables unchanged. | ||
|
|
||
| The default methods are empty and intended to be implemented in extension packages. | ||
| """ | ||
| function variables_flow_resource(m, ℒ::Vector{<:Link}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) end | ||
| function variables_flow_resource(m, 𝒩::Vector{<:Node}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) end | ||
|
|
||
|
|
||
| """ | ||
| variables_opex(m, 𝒩::Vector{<:Node}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel) | ||
| variables_opex(m, ℒ::Vector{<:Link}, 𝒳ᵛᵉᶜ, 𝒯, modeltype::EnergyModel) | ||
|
|
@@ -572,11 +599,41 @@ differentiation in extension packages. | |
| - `Node` - the subfunction is [`create_node`](@ref). | ||
| - `Link` - the subfunction is [`create_link`](@ref). | ||
| """ | ||
| create_element(m, n::Node, 𝒯, 𝒫, modeltype::EnergyModel) = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docstring should be updated for this function as we now actually have specific approaches. This also implies that we want to avoid people to create new methods for This would also be stressed in the documentation. |
||
| function create_element(m, n::Node, 𝒯, 𝒫, modeltype::EnergyModel) | ||
|
|
||
| create_node(m, n, 𝒯, 𝒫, modeltype) | ||
| create_element(m, l::Link, 𝒯, 𝒫, modeltype::EnergyModel) = | ||
|
|
||
| # Constraints based on the resource types | ||
| node_resources = Vector{Resource}(unique(vcat(inputs(n), outputs(n)))) | ||
| for 𝒫ˢᵘᵇ in res_types_seg(node_resources) | ||
| constraints_resource(m, n, 𝒯, 𝒫ˢᵘᵇ, modeltype) | ||
| end | ||
| end | ||
|
|
||
| function create_element(m, l::Link, 𝒯, 𝒫, modeltype::EnergyModel) | ||
|
|
||
| create_link(m, l, 𝒯, 𝒫, modeltype) | ||
|
|
||
| # Constraints based on the resource types | ||
| for 𝒫ˢᵘᵇ in res_types_seg(link_res(l)) | ||
| constraints_resource(m, l, 𝒯, 𝒫ˢᵘᵇ, modeltype) | ||
| end | ||
| end | ||
|
|
||
| """ | ||
| constraints_resource(m, n::Node, 𝒯, 𝒫::Vector{<:Resource}, modeltype::EnergyModel) | ||
| constraints_resource(m, l::Link, 𝒯, 𝒫::Vector{<:Resource}, modeltype::EnergyModel) | ||
|
|
||
| Create constraints for the flow of resources through an [`AbstractElement`](@ref) for | ||
| specific resource types. In `EnergyModelsBase`, this method is provided for | ||
| [`Node`](@ref EnergyModelsBase.Node) and [`Link`](@ref). | ||
|
|
||
| The function is empty by default and can be implemented in extension packages. | ||
| """ | ||
| function constraints_resource(m, n::Node, 𝒯, 𝒫::Vector{<:Resource}, modeltype::EnergyModel) end | ||
|
|
||
| function constraints_resource(m, l::Link, 𝒯, 𝒫::Vector{<:Resource}, modeltype::EnergyModel) end | ||
|
|
||
| """ | ||
| constraints_couple(m, 𝒩::Vector{<:Node}, ℒ::Vector{<:Link}, 𝒫, 𝒯, modeltype::EnergyModel) | ||
| constraints_couple(m, ℒ::Vector{<:Link}, 𝒩::Vector{<:Node}, 𝒫, 𝒯, modeltype::EnergyModel) | ||
|
|
@@ -590,13 +647,15 @@ for the coupling between a [`Link`](@ref) and a [`Node`](@ref EnergyModelsBase.N | |
| function constraints_couple(m, 𝒩::Vector{<:Node}, ℒ::Vector{<:Link}, 𝒫, 𝒯, modeltype::EnergyModel) | ||
| for n ∈ 𝒩 | ||
| ℒᶠʳᵒᵐ, ℒᵗᵒ = link_sub(ℒ, n) | ||
|
|
||
| # Constraint for output flowrate and input links. | ||
| if has_output(n) | ||
| @constraint(m, [t ∈ 𝒯, p ∈ outputs(n)], | ||
| m[:flow_out][n, t, p] == | ||
| sum(m[:link_in][l, t, p] for l ∈ ℒᶠʳᵒᵐ if p ∈ inputs(l)) | ||
| ) | ||
| end | ||
|
|
||
| # Constraint for input flowrate and output links. | ||
| if has_input(n) | ||
| @constraint(m, [t ∈ 𝒯, p ∈ inputs(n)], | ||
|
|
@@ -605,11 +664,29 @@ function constraints_couple(m, 𝒩::Vector{<:Node}, ℒ::Vector{<:Link}, 𝒫, | |
| ) | ||
| end | ||
| end | ||
|
|
||
| # Create new constraints for specific resource types | ||
| for p_sub in res_types_seg(𝒫) | ||
| constraints_couple_resource(m, 𝒩, ℒ, p_sub, 𝒯, modeltype) | ||
| end | ||
| end | ||
| function constraints_couple(m, ℒ::Vector{<:Link}, 𝒩::Vector{<:Node}, 𝒫, 𝒯, modeltype::EnergyModel) | ||
| return constraints_couple(m, 𝒩, ℒ, 𝒫, 𝒯, modeltype) | ||
| end | ||
|
|
||
| """ | ||
| constraints_couple_resource(m, 𝒩::Vector{<:Node}, ℒ::Vector{<:Link}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) | ||
|
|
||
| Create resource-specific coupling constraints between nodes and links. | ||
|
|
||
| This function is called from [`constraints_couple`](@ref) for each subset of resources | ||
| sharing the same type. It can be used to add additional coupling constraints for | ||
| specialized resource classes while keeping the default node-link flow balance unchanged. | ||
|
|
||
| The default method is empty and intended to be implemented in extension packages. | ||
| """ | ||
| function constraints_couple_resource(m, 𝒩::Vector{<:Node}, ℒ::Vector{<:Link}, 𝒫::Vector{<:Resource}, 𝒯, modeltype::EnergyModel) end | ||
|
|
||
| """ | ||
| constraints_emissions(m, 𝒳ᵛᵉᶜ, 𝒫, 𝒯, modeltype::EnergyModel) | ||
|
|
||
|
|
@@ -956,7 +1033,6 @@ function create_link(m, l::Direct, 𝒯, 𝒫, modeltype::EnergyModel) | |
| ) | ||
| end | ||
| function create_link(m, 𝒯, 𝒫, l::Link, modeltype::EnergyModel, formulation::Formulation) | ||
|
|
||
| # Generic link in which each output corresponds to the input | ||
| @constraint(m, [t ∈ 𝒯, p ∈ link_res(l)], | ||
| m[:link_out][l, t, p] == m[:link_in][l, t, p] | ||
|
|
@@ -966,4 +1042,4 @@ function create_link(m, 𝒯, 𝒫, l::Link, modeltype::EnergyModel, formulation | |
| if has_capacity(l) | ||
| constraints_capacity_installed(m, l, 𝒯, modeltype) | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,3 +86,17 @@ Returns all emission resources for a | |
| """ | ||
| res_em(𝒫::Array{<:Resource}) = filter(is_resource_emit, 𝒫) | ||
| res_em(𝒫::Dict) = filter(p -> is_resource_emit(first(p)), 𝒫) | ||
|
|
||
| """ | ||
| res_types(𝒫::Vector{<:Resource}) | ||
|
|
||
| Return the unique resource types in an Vector of resources `𝒫`. | ||
| """ | ||
| res_types(𝒫::Vector{<:Resource}) = unique(map(x -> typeof(x), 𝒫)) | ||
|
|
||
| """ | ||
| res_types_seg(𝒫::Vector{<:Resource}) | ||
|
|
||
| Return a Vector-of-Vectors of resources segmented by the sub-types. | ||
| """ | ||
| res_types_seg(𝒫::Vector{<:Resource}) = [Vector{rt}(filter(x -> isa(x, rt), 𝒫)) for rt in res_types(𝒫)] | ||
|
Comment on lines
+98
to
+102
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that always guaranteed, that you receive a I would suggest renaming the function to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it does not if the input is empty. Then it returns an empty Vector{Any}, but that is ok. Changed to check that it returns a empty vector for a empty input vector instead |
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You do not test that a new |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
|
|
||
| Power = ResourceCarrier("Power", 0.0) | ||
| Heat = ResourceCarrier("Heat", 0.0) | ||
| CO2 = ResourceEmit("CO2", 1.0) | ||
|
|
||
| 𝒫 = [Power, Heat, CO2] | ||
|
|
||
| @testset "Resource - get resource types" begin | ||
| # returns a Vector of DataTypes | ||
| @test typeof(EMB.res_types(𝒫)) == Vector{DataType} | ||
|
|
||
| # returns the correct number of unique resource types | ||
| @test length(EMB.res_types(𝒫)) == 2 | ||
| end | ||
|
|
||
| @testset "Resource - get resource vectors by type" begin | ||
| # returns a Vector of Vectors | ||
| @test typeof(EMB.res_types_seg(𝒫)) == Vector{Vector} | ||
|
|
||
| # returns the correct number of segments | ||
| @test length(EMB.res_types_seg(𝒫)) == 2 | ||
|
|
||
| # the length of the first segment should be 2 (2 ResourceCarriers) | ||
| @test length(EMB.res_types_seg(𝒫)[1]) == 2 | ||
|
|
||
| # the length of the second segment should be 1 (1 ResourceEmit) | ||
| @test length(EMB.res_types_seg(𝒫)[2]) == 1 | ||
|
|
||
| end | ||
|
|
||
| # Add a new resource type and check that it is correctly identified by res_types and res_types_seg | ||
| struct TestResource <: Resource | ||
| id::String | ||
| a::Float64 | ||
| b::Int64 | ||
| end | ||
|
|
||
| # Add a new resource of type TestResource to the resource vector | ||
| 𝒫 = vcat(𝒫, [TestResource("Test", 0.5, 1)]) | ||
|
|
||
| @testset "Resource - get resource types w/ custom resource type" begin | ||
| # returns a Vector of DataTypes (now including TestResource) | ||
| @test typeof(EMB.res_types(𝒫)) == Vector{DataType} | ||
|
|
||
| # returns the correct number of unique resource types (now 3) | ||
| @test length(EMB.res_types(𝒫)) == 3 | ||
|
|
||
| end | ||
|
|
||
| @testset "Resource - get resource vectors by type w/ custom resource type" begin | ||
| # returns the correct number of segments (now 3) | ||
| @test length(EMB.res_types_seg(𝒫)) == 3 | ||
|
|
||
| # the length of the first segment should be 2 (2 ResourceCarriers) | ||
| @test length(EMB.res_types_seg(𝒫)[1]) == 2 | ||
|
|
||
| # the length of the second segment should be 1 (1 ResourceEmit) | ||
| @test length(EMB.res_types_seg(𝒫)[2]) == 1 | ||
|
|
||
| # the length of the third segment should be 1 (1 TestResource) | ||
| @test length(EMB.res_types_seg(𝒫)[3]) == 1 | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.