From 4ac7fa6e42ef73b94f245a036f6379c526f6545e Mon Sep 17 00:00:00 2001 From: Julian Straus <104911227+JulStraus@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:38:01 +0100 Subject: [PATCH] Updated restrictions in types and functions (#9) * Updated restrictions in types and functions * Uniform argument indents for function calls --- NEWS.md | 4 + Project.toml | 2 +- README.md | 2 +- examples/network.jl | 3 - examples/sink_source.jl | 1 - src/constraint_functions.jl | 8 +- src/data_functions.jl | 2 +- src/legacy_constructor.jl | 24 ++--- src/model.jl | 14 +-- src/structures_data.jl | 32 +++---- src/structures_link.jl | 4 +- src/structures_model.jl | 51 ++++++----- src/structures_node.jl | 170 +++++++++++++++++++++--------------- test/example_model.jl | 14 +-- test/modeltype.jl | 1 - test/nodes.jl | 25 +----- test/test_utils.jl | 20 +++-- 17 files changed, 199 insertions(+), 178 deletions(-) diff --git a/NEWS.md b/NEWS.md index 9fa3c3e..3280264 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # Release notes +Version 0.6.5 (2024-01-31) +-------------------------- + * Updated the restrictions on the fields of individual types to be consistent. + Version 0.6.4 (2024-01-18) -------------------------- * Minor modification to the `EmissionsData` allowing now also time dependent process emissions. This is achieved through switching to a parametric type. diff --git a/Project.toml b/Project.toml index 7f0ed57..a2c42d8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "EnergyModelsBase" uuid = "5d7e687e-f956-46f3-9045-6f5a5fd49f50" authors = ["Lars Hellemo , Julian Straus "] -version = "0.6.4" +version = "0.6.5" [deps] JuMP = "4076af6c-e467-56ae-b986-b466b2749572" diff --git a/README.md b/README.md index 7a7dd5e..ce00981 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # EnergyModelsBase [![Build Status](https://github.com/EnergyModelsX/EnergyModelsBase.jl/workflows/CI/badge.svg)](https://github.com/EnergyModelsX/EnergyModelsBase.jl/actions?query=workflow%3ACI) -[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://energymodelsx.github.io/EnergyModelsBase.jl//stable) +[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://energymodelsx.github.io/EnergyModelsBase.jl/stable/) [![In Development](https://img.shields.io/badge/docs-dev-blue.svg)](https://energymodelsx.github.io/EnergyModelsBase.jl/dev/) `EnergyModelsBase` is the core package for building flexible multi-energy-carrier energy systems models. diff --git a/examples/network.jl b/examples/network.jl index 2d993c1..7afc61a 100644 --- a/examples/network.jl +++ b/examples/network.jl @@ -62,7 +62,6 @@ function generate_data() FixedProfile(30), # Variable OPEX in EUR/MW FixedProfile(0), # Fixed OPEX in EUR/8h Dict(NG => 1), # Output from the Node, in this gase, NG - [], # Potential additional data ), RefSource( 3, # Node id @@ -70,7 +69,6 @@ function generate_data() FixedProfile(9), # Variable OPEX in EUR/MWh FixedProfile(0), # Fixed OPEX in EUR/8h Dict(Coal => 1), # Output from the Node, in this gase, coal - [], # Potential additional data ), RefNetworkNode( 4, # Node id @@ -103,7 +101,6 @@ function generate_data() # Line above: This implies that storing COโ‚‚ requires Power Dict(CO2 => 1), # Output from the node with output ratio # In practice, for COโ‚‚ storage, this is never used. - Array{Data}([]), # Potential additional data ), RefSink( 7, # Node id diff --git a/examples/sink_source.jl b/examples/sink_source.jl index aec87c0..198a42a 100644 --- a/examples/sink_source.jl +++ b/examples/sink_source.jl @@ -49,7 +49,6 @@ function generate_data() FixedProfile(30), # Variable OPEX in EUR/MW FixedProfile(0), # Fixed OPEX in EUR/8h Dict(Power => 1), # Output from the Node, in this gase, Power - [], # Potential additional data ), RefSink( 2, # Node id diff --git a/src/constraint_functions.jl b/src/constraint_functions.jl index bf32d2d..fd76a50 100644 --- a/src/constraint_functions.jl +++ b/src/constraint_functions.jl @@ -213,7 +213,7 @@ function constraints_level_sp( t_inv::TS.StrategicPeriod{T, U}, ๐’ซ, modeltype::EnergyModel - ) where {S<:ResourceCarrier, T, U<:SimpleTimes} +) where {S<:ResourceCarrier, T, U<:SimpleTimes} # Mass/energy balance constraints for stored energy carrier. for (t_prev, t) โˆˆ withprev(t_inv) @@ -252,7 +252,7 @@ function constraints_level_sp( t_inv::TS.StrategicPeriod{T, RepresentativePeriods{U, T, SimpleTimes{T}}}, ๐’ซ, modeltype::EnergyModel - ) where {S<:ResourceCarrier, T, U} +) where {S<:ResourceCarrier, T, U} # Declaration of the required subsets ๐’ฏสณแต– = repr_periods(t_inv) @@ -350,7 +350,7 @@ function constraints_level_sp( t_inv::TS.StrategicPeriod{T, U}, ๐’ซ, modeltype::EnergyModel - ) where {S<:ResourceEmit, T, U<:SimpleTimes} +) where {S<:ResourceEmit, T, U<:SimpleTimes} # Mass/energy balance constraints for stored energy carrier. for (t_prev, t) โˆˆ withprev(t_inv) @@ -388,7 +388,7 @@ function constraints_level_sp( t_inv::TS.StrategicPeriod{T, RepresentativePeriods{U, T, SimpleTimes{T}}}, ๐’ซ, modeltype::EnergyModel - ) where {S<:ResourceEmit, T, U} +) where {S<:ResourceEmit, T, U} # Declaration of the required subsets ๐’ฏสณแต– = repr_periods(t_inv) diff --git a/src/data_functions.jl b/src/data_functions.jl index 20ec636..2fe0303 100644 --- a/src/data_functions.jl +++ b/src/data_functions.jl @@ -7,7 +7,7 @@ process. There exist several configurations: - **`EmissionsEnergy`**: Only energy usage related emissions.\n - **`EmissionsProcess`**: Both process and energy usage related emissions.\n -- **`CaptureEnergyEmissions`**: Capture of energy usage related emissions, can include +- **`CaptureEnergyEmissions`**: Capture of energy usage related emissions, can include \ process emissions.\n - **`CaptureProcessEmissions`**: Capture of process emissions.\n - **`CaptureProcessEnergyEmissions`**: Capture of both process and energy usage related diff --git a/src/legacy_constructor.jl b/src/legacy_constructor.jl index 3aa7be1..42fffc7 100644 --- a/src/legacy_constructor.jl +++ b/src/legacy_constructor.jl @@ -11,9 +11,9 @@ function RefSource( opex_var::TimeProfile, opex_fixed::TimeProfile, output::Dict{<:Resource,<:Real}, - data::Array, + data::Vector, emissions::Dict{<:ResourceEmit,<:Real}, - ) +) @warn("This implementation of a `RefSource` will be discontinued in the near future. \ See the documentation for the new implementation using the `data` field. @@ -36,8 +36,8 @@ function RefNetwork( opex_fixed::TimeProfile, input::Dict{<:Resource,<:Real}, output::Dict{<:Resource,<:Real}, - data::Array, - ) + data::Vector, +) @warn("The implementation of a `RefNetwork` will be discontinued in \ the near future. The name is replaced by RefNetworkNode, while the fields remain \ @@ -63,8 +63,8 @@ function RefNetworkEmissions( output::Dict{<:Resource,<:Real}, emissions::Dict{<:ResourceEmit,<:Real}, co2_capture::Real, - data::Array, - ) + data::Vector, +) @warn("The implementation of a `RefNetworkEmissions` will be discontinued in \ the near future. See the documentation for the new implementation using the `data` \ @@ -93,8 +93,8 @@ function RefStorageEmissions( stor_res::ResourceEmit, input::Dict{<:Resource,<:Real}, output::Dict{<:Resource,<:Real}, - data::Array, - ) + data::Vector, +) @warn("The implementation of a `RefStorageEmissions` will be discontinued in \ the near future. See the documentation for the new implementation using a parametric \ @@ -111,7 +111,7 @@ function RefStorageEmissions( stor_res, input, output, - Array{Data}(data), + Vector{Data}(data), ) return tmp end @@ -126,7 +126,7 @@ function GenAvailability( id, input::Dict{<:Resource,<:Real}, output::Dict{<:Resource,<:Real}, - ) +) @warn("This implementation of a `GenAvailability` will be discontinued in \ the near future. See the documentation for the new implementation not requiring using \ @@ -148,7 +148,7 @@ function RefSink( penalty::Dict{<:Any,<:TimeProfile}, input::Dict{<:Resource,<:Real}, emissions::Dict{<:ResourceEmit,<:Real}, - ) +) @warn("This implementation of a `RefSink` will be discontinued in the near future. \ See the documentation for the new implementation using the `data` field. \ @@ -171,7 +171,7 @@ price. function OperationalModel( emission_limit::Dict{<:ResourceEmit, <:TimeProfile}, co2_instance::ResourceEmit, - ) +) @warn("this implementation of `OperationalModel` will be discontinued in the near \ future. See the documentation for the new implementation with the additional field: diff --git a/src/model.jl b/src/model.jl index 7a28ca0..6c1de38 100644 --- a/src/model.jl +++ b/src/model.jl @@ -106,8 +106,8 @@ resource `๐’ซแต‰แต โˆˆ ๐’ซ`. The emission variables are differentiated in: * `:emissions_node` - emissions of a node in an operational period, * `:emissions_total` - total emissions in an operational period, and - * `:emissions_strategic` - total strategic emissions, constrained to an upper limit based\ - on the field `emission_limit` of the `EnergyModel`. + * `:emissions_strategic` - total strategic emissions, constrained to an upper limit \ + based on the field `emission_limit` of the `EnergyModel`. """ function variables_emission(m, ๐’ฉ, ๐’ฏ, ๐’ซ, modeltype::EnergyModel) @@ -124,7 +124,7 @@ end """ variables_opex(m, ๐’ฉ, ๐’ฏ, ๐’ซ, modeltype::EnergyModel) -Declaration of the OPEX variables (`:opex_var` and `:opex_fixed`) of the model for each investment +Declaration of the OPEX variables (`:opex_var` and `:opex_fixed`) of the model for each period `๐’ฏแดตโฟแต› โˆˆ ๐’ฏ`. Variable OPEX can be non negative to account for revenue streams. """ function variables_opex(m, ๐’ฉ, ๐’ฏ, ๐’ซ, modeltype::EnergyModel) @@ -413,8 +413,8 @@ Set all constraints for a `Availability`. Can serve as fallback option for all u subtypes of `Availability`. Availability nodes can be seen as routing nodes. It is not necessary to have more than one -available node except if one wants to include as well transport between different availability -nodes with associated costs (not implemented at the moment). +available node except if one wants to include as well transport between different +`Availability` nodes with associated costs (not implemented at the moment). """ function create_node(m, n::Availability, ๐’ฏ, ๐’ซ, modeltype::EnergyModel) @@ -426,8 +426,8 @@ end """ create_link(m, ๐’ฏ, ๐’ซ, l, formulation::Formulation) -Set the constraints for a simple `Link` (input = output). Can serve as fallback option for all -unspecified subtypes of `Link`. +Set the constraints for a simple `Link` (input = output). Can serve as fallback option for +all unspecified subtypes of `Link`. """ function create_link(m, ๐’ฏ, ๐’ซ, l, formulation::Formulation) diff --git a/src/structures_data.jl b/src/structures_data.jl index f8edb0a..7d1c905 100644 --- a/src/structures_data.jl +++ b/src/structures_data.jl @@ -16,14 +16,14 @@ an input as `TimeProfile` or `Float64`.\n - **`co2_capture::Float64`**: COโ‚‚ capture rate.\n # Types -- **`CaptureProcessEnergyEmissions`**: Capture both the process emissions and the energy usage \ -related emissions.\n -- **`CaptureProcessEmissions`**: Capture the process emissions, but not the energy usage related \ -emissions.\n -- **`CaptureEnergyEmissions`**: Capture the energy usage related emissions, but not the process \ -emissions. Does not require `emissions` as input.\n -- **`EmissionsProcess`**: No capture, but process emissions are present. Does not require \ -`co2_capture` as input, but will ignore it, if provided.\n +- **`CaptureProcessEnergyEmissions`**: Capture both the process emissions and the \ +energy usage related emissions.\n +- **`CaptureProcessEmissions`**: Capture the process emissions, but not the \ +energy usage related emissions.\n +- **`CaptureEnergyEmissions`**: Capture the energy usage related emissions, but not the \ +process emissions. Does not require `emissions` as input.\n +- **`EmissionsProcess`**: No capture, but process emissions are present. \ +Does not require `co2_capture` as input, but will ignore it, if provided.\n - **`EmissionsEnergy`**: No capture and no process emissions. Does not require \ `co2_capture` or `emissions` as input, but will ignore them, if provided.\n """ @@ -108,28 +108,28 @@ Returns the `ResourceEmit`s that have process emissions in the `data`. process_emissions(data::EmissionsData) = collect(keys(data.emissions)) """ - process_emissions(data::EmissionsData, p) + process_emissions(data::EmissionsData{T}, p::ResourceEmit) Returns the the process emissions of resource `p` in the `data` as `TimeProfile`. If the process emissions are provided as `Float64`, it returns a FixedProfile(x). If there are no process emissions, it returns a FixedProfile(0). """ -process_emissions(data::EmissionsData{T}, p) where {T<:Float64} = +process_emissions(data::EmissionsData{T}, p::ResourceEmit) where {T<:Float64} = haskey(data.emissions, p) ? FixedProfile(data.emissions[p]) : FixedProfile(0) -process_emissions(data::EmissionsData{T}, p) where {T<:TimeProfile} = +process_emissions(data::EmissionsData{T}, p::ResourceEmit) where {T<:TimeProfile} = haskey(data.emissions, p) ? data.emissions[p] : FixedProfile(0) -process_emissions(data::EmissionsEnergy{T}, p) where {T} = +process_emissions(data::EmissionsEnergy{T}, p::ResourceEmit) where {T} = @error("The type `EmissionsEnergy` should not be used in combination with calling \ the function `process_emissions`.") """ - process_emissions(data::EmissionsData, p, t) + process_emissions(data::EmissionsData{T}, p:ResourceEmit, t) Returns the the process emissions of resource `p` in the `data` at operational period t. If there are no process emissions, it returns a value of 0. """ -process_emissions(data::EmissionsData{T}, p, t) where {T<:Float64} = +process_emissions(data::EmissionsData{T}, p::ResourceEmit, t) where {T<:Float64} = haskey(data.emissions, p) ? data.emissions[p] : 0 -process_emissions(data::EmissionsData{T}, p, t) where {T<:TimeProfile} = +process_emissions(data::EmissionsData{T}, p::ResourceEmit, t) where {T<:TimeProfile} = haskey(data.emissions, p) ? data.emissions[p][t] : 0 -process_emissions(data::EmissionsEnergy{T}, p, t) where {T} = +process_emissions(data::EmissionsEnergy{T}, p::ResourceEmit, t) where {T} = @error("The type `EmissionsEnergy` should not be used in combination with calling \ the function `process_emissions`.") diff --git a/src/structures_link.jl b/src/structures_link.jl index d992472..1a0d177 100644 --- a/src/structures_link.jl +++ b/src/structures_link.jl @@ -30,11 +30,11 @@ Direct(id, from::Node, to::Node) = Direct(id, from, to, Linear()) """ - link_sub(โ„’, n::Node) + link_sub(โ„’::Vector{<:Link}, n::Node) Return connected links for a given node `n`. """ -function link_sub(โ„’, n::Node) +function link_sub(โ„’::Vector{<:Link}, n::Node) return [โ„’[findall(x -> x.from == n, โ„’)], โ„’[findall(x -> x.to == n, โ„’)]] end diff --git a/src/structures_model.jl b/src/structures_model.jl index 8b896a9..48e1ed1 100644 --- a/src/structures_model.jl +++ b/src/structures_model.jl @@ -5,65 +5,68 @@ abstract type EnergyModel end Operational Energy Model without investments. # Fields -- **`emission_limit`** is a dictionary with individual emission limits as `TimeProfile` for \ -each emission resource `ResourceEmit`.\n -- **`emission_price::Dict{ResourceEmit, TimeProfile}`** are the prices for the different \ -emissions types considered.\n +- **`emission_limit::Dict{<:ResourceEmit, <:TimeProfile}`** is a dictionary with \ +individual emission limits as `TimeProfile` for each emission resource `ResourceEmit`.\n +- **`emission_price::Dict{<:ResourceEmit, <:TimeProfile}`** are the prices for the \ +different emissions types considered.\n - **`co2_instance`** is a `ResourceEmit` and corresponds to the type used for COโ‚‚.\n """ struct OperationalModel <: EnergyModel - emission_limit::Dict{ResourceEmit, TimeProfile} - emission_price::Dict{ResourceEmit, TimeProfile} + emission_limit::Dict{<:ResourceEmit, <:TimeProfile} + emission_price::Dict{<:ResourceEmit, <:TimeProfile} co2_instance::ResourceEmit end """ - emission_limit(model) + emission_limit(model::EnergyModel) Returns the emission limit of EnergyModel `model` as dictionary with `TimeProfile`s for each `ResourceEmit`. """ -emission_limit(model) = model.emission_limit +emission_limit(model::EnergyModel) = model.emission_limit """ - emission_limit(model, p) + emission_limit(model::EnergyModel, p::ResourceEmit) Returns the emission limit of EnergyModel `model` and ResourceEmit `p` as `TimeProfile`. """ -emission_limit(model, p) = model.emission_limit[p] +emission_limit(model::EnergyModel, p::ResourceEmit) = model.emission_limit[p] """ - emission_limit(model, p, t) + emission_limit(model::EnergyModel, p::ResourceEmit, t_inv::TS.StrategicPeriod) -Returns the emission limit of EnergyModel `model` and ResourceEmit `p` in operational -period `t`. +Returns the emission limit of EnergyModel `model` and ResourceEmit `p` in strategic period +period `t_inv`. """ -emission_limit(model, p, t) = model.emission_limit[p][t] +emission_limit(model::EnergyModel, p::ResourceEmit, t_inv::TS.StrategicPeriod) = + model.emission_limit[p][t_inv] """ - emission_price(model) + emission_price(model::EnergyModel) Returns the emission price of EnergyModel `model` as dictionary with `TimeProfile`s for each `ResourceEmit`. """ -emission_price(model) = model.emission_price +emission_price(model::EnergyModel) = model.emission_price """ - emission_price(model, p) + emission_price(model::EnergyModel, p::ResourceEmit) Returns the emission price of EnergyModel `model` and ResourceEmit `p` as `TimeProfile`. If no emission price is specified for the ResourceEmit `p`, the function returns 0 """ -emission_price(model, p) = haskey(model.emission_price, p) ? model.emission_price[p] : 0 +emission_price(model::EnergyModel, p::ResourceEmit) = + haskey(model.emission_price, p) ? model.emission_price[p] : 0 """ - emission_price(model, p, t) + emission_price(model::EnergyModel, p::ResourceEmit, t_inv::TS.StrategicPeriod) -Returns the emission price of EnergyModel `model` and ResourceEmit `p` in operational -period `t`. +Returns the emission price of EnergyModel `model` and ResourceEmit `p` in strategic +period `t_inv`. If no emission price is specified for the ResourceEmit `p`, the function returns 0 """ -emission_price(model, p, t) = haskey(model.emission_price, p) ? model.emission_price[p][t] : 0 +emission_price(model::EnergyModel, p::ResourceEmit, t_inv::TS.StrategicPeriod) = + haskey(model.emission_price, p) ? model.emission_price[p][t_inv] : 0 """ - co2_instance(model) + co2_instance(model::EnergyModel) Returns the COโ‚‚ instance used in modelling. """ -co2_instance(model) = model.co2_instance +co2_instance(model::EnergyModel) = model.co2_instance diff --git a/src/structures_node.jl b/src/structures_node.jl index e67af11..4e5763b 100644 --- a/src/structures_node.jl +++ b/src/structures_node.jl @@ -20,8 +20,9 @@ abstract type Availability <: NetworkNode end - **`cap::TimeProfile`** is the installed capacity.\n - **`opex_var::TimeProfile`** is the variational operational costs per energy unit produced.\n - **`opex_fixed::TimeProfile`** is the fixed operational costs.\n -- **`output::Dict{Resource, Real}`** are the generated `Resource`s with conversion value `Real`.\n -- **`data::Array{Data}`** is the additional data (e.g. for investments). +- **`output::Dict{<:Resource, <:Real}`** are the generated `Resource`s with conversion value `Real`.\n +- **`data::Vector{Data}`** is the additional data (e.g. for investments). The field \ +`data` is conditional through usage of a constructor. """ struct RefSource <: Source @@ -29,8 +30,8 @@ struct RefSource <: Source cap::TimeProfile opex_var::TimeProfile opex_fixed::TimeProfile - output::Dict{Resource, Real} - data::Array{Data} + output::Dict{<:Resource, <:Real} + data::Vector{Data} end function RefSource( id, @@ -38,8 +39,8 @@ function RefSource( opex_var::TimeProfile, opex_fixed::TimeProfile, output::Dict{<:Resource,<:Real}, - ) - return RefSource(id, cap, opex_var, opex_fixed, output, []) +) + return RefSource(id, cap, opex_var, opex_fixed, output, Data[]) end """ A reference `NetworkNode` node. @@ -49,37 +50,48 @@ end - **`cap::TimeProfile`** is the installed capacity.\n - **`opex_var::TimeProfile`** is the variational operational costs per energy unit produced.\n - **`opex_fixed::TimeProfile`** is the fixed operational costs.\n -- **`input::Dict{Resource, Real}`** are the input `Resource`s with conversion value `Real`.\n -- **`output::Dict{Resource, Real}`** are the generated `Resource`s with conversion value `Real`.\n -- **`data::Array{Data}`** is the additional data (e.g. for investments). +- **`input::Dict{<:Resource, <:Real}`** are the input `Resource`s with conversion value `Real`.\n +- **`output::Dict{<:Resource, <:Real}`** are the generated `Resource`s with conversion value `Real`.\n +- **`data::Vector{Data}`** is the additional data (e.g. for investments). The field \ +`data` is conditional through usage of a constructor. """ struct RefNetworkNode <: NetworkNode id cap::TimeProfile opex_var::TimeProfile opex_fixed::TimeProfile - input::Dict{Resource, Real} - output::Dict{Resource, Real} - data::Array{Data} + input::Dict{<:Resource, <:Real} + output::Dict{<:Resource, <:Real} + data::Vector{Data} +end +function RefNetworkNode( + id, + cap::TimeProfile, + opex_var::TimeProfile, + opex_fixed::TimeProfile, + input::Dict{<:Resource, <:Real}, + output::Dict{<:Resource, <:Real}, +) + return RefNetworkNode(id, cap, opex_var, opex_fixed, input, output, Data[]) end """ A reference `Availability` node. # Fields - **`id`** is the name/identifier of the node.\n -- **`inputs::Array{Resource}`** are the input `Resource`s.\n -- **`output::Array{Resource}`** are the output `Resource`s.\n +- **`inputs::Vector{<:Resource}`** are the input `Resource`s.\n +- **`output::Vector{<:Resource}`** are the output `Resource`s.\n A constructor is provided so that only a single array can be provided with the fields: - **`id`** is the name/identifier of the node.\n -- **`๐’ซ::Array{Resource}`** are the `Resource`s.\n +- **`๐’ซ::Vector{<:Resource}`** are the `Resource`s.\n """ struct GenAvailability <: Availability id - input::Array{Resource} - output::Array{Resource} + input::Vector{<:Resource} + output::Vector{<:Resource} end -GenAvailability(id, ๐’ซ::Array{Resource}) = GenAvailability(id, ๐’ซ, ๐’ซ) +GenAvailability(id, ๐’ซ::Vector{<:Resource}) = GenAvailability(id, ๐’ซ, ๐’ซ) """ A reference `Storage` node. @@ -92,13 +104,14 @@ It is designed as a composite type to automatically distinguish between these tw - **`stor_cap::TimeProfile`** is the installed storage capacity, that is e.g. energy or mass.\n - **`opex_var::TimeProfile`** is the variational operational costs per energy unit stored.\n - **`opex_fixed::TimeProfile`** is the fixed operational costs.\n -- **`stor_res::Resource`** is the stored `Resource`.\n -- **`input::Dict{Resource, Real}`** are the input `Resource`s with conversion value `Real`. -- **`output::Dict{Resource, Real}`** are the generated `Resource`s with conversion value `Real`. \ +- **`stor_res::T`** is the stored `Resource`.\n +- **`input::Dict{<:Resource, <:Real}`** are the input `Resource`s with conversion value `Real`. +- **`output::Dict{<:Resource, <:Real}`** are the generated `Resource`s with conversion value `Real`. \ Only relevant for linking and the stored `Resource`.\n -- **`data::Array{Data}`** is the additional data (e.g. for investments). +- **`data::Vector{<:Data}`** is the additional data (e.g. for investments). The field \ +`data` is conditional through usage of a constructor. """ -struct RefStorage{T} <: Storage +struct RefStorage{T<:Resource} <: Storage id rate_cap::TimeProfile stor_cap::TimeProfile @@ -107,7 +120,29 @@ struct RefStorage{T} <: Storage stor_res::T input::Dict{<:Resource, <:Real} output::Dict{<:Resource, <:Real} - data::Array{<:Data} + data::Vector{<:Data} +end +function RefStorage( + id, + rate_cap::TimeProfile, + stor_cap::TimeProfile, + opex_var::TimeProfile, + opex_fixed::TimeProfile, + stor_res::T, + input::Dict{<:Resource, <:Real}, + output::Dict{<:Resource, <:Real}, +) where {T<:Resource} + return RefStorage( + id, + rate_cap, + stor_cap, + opex_var, + opex_fixed, + stor_res, + input, + output, + Data[], + ) end """ A reference `Sink` node. @@ -119,23 +154,24 @@ This node corresponds to a demand given by the field `cap`. - **`cap::TimeProfile`** is the Demand.\n - **`penalty::Dict{Any, TimeProfile}`** are penalties for surplus or deficits. \ Requires the fields `:surplus` and `:deficit`.\n -- **`input::Dict{Resource, Real}`** are the input `Resource`s with conversion value `Real`.\n -- **`data::Array{Data}`** is the additional data (e.g. for investments). +- **`input::Dict{<:Resource, <:Real}`** are the input `Resource`s with conversion value `Real`.\n +- **`data::Vector{Data}`** is the additional data (e.g. for investments). The field \ +`data` is conditional through usage of a constructor. """ struct RefSink <: Sink id cap::TimeProfile - penalty::Dict{Any, TimeProfile} - input::Dict{Resource, Real} - data::Array{Data} + penalty::Dict{Symbol, <:TimeProfile} + input::Dict{<:Resource, <:Real} + data::Vector{Data} end function RefSink( id, cap::TimeProfile, penalty::Dict{<:Any,<:TimeProfile}, input::Dict{<:Resource,<:Real}, - ) - return RefSink(id, cap, penalty, input, []) +) + return RefSink(id, cap, penalty, input, Data[]) end """ @@ -172,11 +208,11 @@ is_sink(n::Sink) = true """ - nodes_sub(๐’ฉ::Array{Node}, sub/subs) + nodes_sub(๐’ฉ::Array{<:Node}, sub/subs) -Return nodes that are of type sub/subs for a given Array `::Array{Node}`. +Return nodes that are of type sub/subs for a given Array `::Array{<:Node}`. """ -node_sub(๐’ฉ::Array{Node}, sub = NetworkNode) = ๐’ฉ[findall(x -> isa(x, sub), ๐’ฉ)] +node_sub(๐’ฉ::Array{<:Node}, sub = NetworkNode) = ๐’ฉ[findall(x -> isa(x, sub), ๐’ฉ)] """ has_emissions(n::Node) @@ -188,32 +224,28 @@ has_emissions(n::Availability) = false has_emissions(n::RefStorage{<:ResourceEmit}) = true """ - has_emissions(๐’ฉ::Array{Node}) + has_emissions(๐’ฉ::Array{<:Node}) -Return nodes that have emission data for a given Array `::Array{Node}`. +Return nodes that have emission data for a given Array `::Array{<:Node}`. """ -nodes_emissions(๐’ฉ::Array{Node}) = filter(has_emissions, ๐’ฉ) +nodes_emissions(๐’ฉ::Array{<:Node}) = filter(has_emissions, ๐’ฉ) """ - nodes_not_sub(๐’ฉ::Array{Node}, sub) + nodes_not_sub(๐’ฉ::Array{<:Node}, sub) -Return nodes that are not of type `sub` for a given Array `::Array{Node}`. +Return nodes that are not of type `sub` for a given Array `::Array{<:Node}`. """ -nodes_not_sub(๐’ฉ::Array{Node}, sub = NetworkNode) = filter(x -> ~isa(x, sub), ๐’ฉ) +nodes_not_sub(๐’ฉ::Array{<:Node}, sub = NetworkNode) = filter(x -> ~isa(x, sub), ๐’ฉ) """ - nodes_not_av(๐’ฉ::Array{Node}) + nodes_not_av(๐’ฉ::Array{<:Node}) -Return nodes that are not `Availability` nodes for a given Array `::Array{Node}`. +Return nodes that are not `Availability` nodes for a given Array `::Array{<:Node}`. """ -nodes_not_av(๐’ฉ::Array{Node}) = filter(x -> ~isa(x, Availability), ๐’ฉ) - -# function node_sub(๐’ฉ::Array{Node}, subs...) -# return ๐’ฉ[findall(x -> sum(isa(x, sub) for sub in subs) >= 1, ๐’ฉ)] -# end +nodes_not_av(๐’ฉ::Array{<:Node}) = filter(x -> ~isa(x, Availability), ๐’ฉ) """ - nodes_input(๐’ฉ::Array{Node}, sub) + nodes_input(๐’ฉ::Array{<:Node}, sub) Return nodes that have an input, i.e., `Sink` and `NetworkNode` nodes. """ @@ -228,7 +260,7 @@ has_input(n::Node) = true has_input(n::Source) = false """ - nodes_output(๐’ฉ::Array{Node}) + nodes_output(๐’ฉ::Array{<:Node}) Return nodes that have an output, i.e., `Source` and `NetworkNode` nodes. """ @@ -243,7 +275,7 @@ has_output(n::Node) = true has_output(n::Sink) = false """ - capacity(n) + capacity(n::Node) Returns the capacity of a node `n` as `TimeProfile`. In the case of a `Storage` node, the capacity is returned as `NamedTuple` with the fields `level` and `rate`. @@ -252,16 +284,16 @@ capacity(n::Node) = n.cap capacity(n::Storage) = (level=n.stor_cap, rate=n.rate_cap) """ - capacity(n, t) + capacity(n::Node, t) -Returns the capacity of a node `n` at time period `t`. In the case of a `Storage` node, -the capacity is returned as `NamedTuple` with the fields `level` and `rate`. +Returns the capacity of a node `n` at operational period `t`. In the case of a `Storage` +node, the capacity is returned as `NamedTuple` with the fields `level` and `rate`. """ capacity(n::Node, t) = n.cap[t] capacity(n::Storage, t) = (level=n.stor_cap[t], rate=n.rate_cap[t]) """ - inputs(n) + inputs(n::Node) Returns the input resources of a node `n`. These resources are specified via the field `input`. @@ -271,15 +303,15 @@ inputs(n::Availability) = n.input inputs(n::Source) = [] """ - inputs(n, p) + inputs(n::Node, p::Resource) Returns the value of an input resource `p` of a node `n`. """ -inputs(n::Node, p) = n.input[p] -inputs(n::Source, p) = nothing +inputs(n::Node, p::Resource) = n.input[p] +inputs(n::Source, p::Resource) = nothing """ - outputs(n) + outputs(n::Node) Returns the output resources of a node `n`. These resources are specified via the field `output`. @@ -288,11 +320,11 @@ outputs(n::Node) = collect(keys(n.output)) outputs(n::Availability) = n.output """ - outputs(n, p) + outputs(n::Node, p::Resource) Returns the value of an output resource `p` of a node `n`. """ -outputs(n::Node, p) = n.output[p] +outputs(n::Node, p::Resource) = n.output[p] """ node_data(n::Node) @@ -309,30 +341,30 @@ Returns the storage resource of `Storage` node `n`. storage_resource(n::Storage) = n.stor_res """ - opex_var(n) + opex_var(n::Node) Returns the variable OPEX of a node `n` as `TimeProfile`. """ opex_var(n::Node) = n.opex_var """ - opex_var(n, t) + opex_var(n::Node, t) -Returns the variable OPEX of a node `n` at time period `t` +Returns the variable OPEX of a node `n` in operational period `t` """ opex_var(n::Node, t) = n.opex_var[t] """ - opex_fixed(n) + opex_fixed(n::Node) Returns the fixed OPEX of a node `n` as `TimeProfile`. """ opex_fixed(n::Node) = n.opex_fixed """ - opex_fixed(n, t) + opex_fixed(n::Node, t_inv) -Returns the fixed OPEX of a node `n` at time period `t` +Returns the fixed OPEX of a node `n` at strategic period `t_inv` """ -opex_fixed(n::Node, t) = n.opex_fixed[t] +opex_fixed(n::Node, t_inv) = n.opex_fixed[t_inv] """ surplus_penalty(n::Sink) @@ -341,7 +373,7 @@ Returns the surplus penalty of sink `n` as `TimeProfile`. surplus_penalty(n::Sink) = n.penalty[:surplus] """ surplus_penalty(n::Sink, t) -Returns the surplus penalty of sink `n` at time period `t` +Returns the surplus penalty of sink `n` at operational period `t` """ surplus_penalty(n::Sink, t) = n.penalty[:surplus][t] @@ -352,6 +384,6 @@ Returns the deficit penalty of sink `n` as `TimeProfile`. deficit_penalty(n::Sink) = n.penalty[:deficit] """ deficit_penalty(n::Sink, t) -Returns the deficit penalty of sink `n` at time period `t` +Returns the deficit penalty of sink `n` at operational period `t` """ deficit_penalty(n::Sink, t) = n.penalty[:deficit][t] diff --git a/test/example_model.jl b/test/example_model.jl index 4abead4..17fcc0e 100644 --- a/test/example_model.jl +++ b/test/example_model.jl @@ -17,25 +17,27 @@ function generate_data() GenAvailability(1, products), RefSource(2, FixedProfile(1e12), FixedProfile(30), FixedProfile(0), Dict(NG => 1), - []), + ), RefSource(3, FixedProfile(1e12), FixedProfile(9), FixedProfile(0), Dict(Coal => 1), - []), + ), RefNetworkNode(4, FixedProfile(25), FixedProfile(5.5), FixedProfile(5), Dict(NG => 2), Dict(Power => 1, CO2 => 1), - [capture_data]), + [capture_data], + ), RefNetworkNode(5, FixedProfile(25), FixedProfile(6), FixedProfile(10), Dict(Coal => 2.5), Dict(Power => 1), - [emission_data]), + [emission_data], + ), RefStorage(6, FixedProfile(60), FixedProfile(600), FixedProfile(9.1), FixedProfile(0), CO2, Dict(CO2 => 1, Power => 0.02), Dict(CO2 => 1), - Array{Data}([])), + ), RefSink(7, OperationalProfile([20 30 40 30]), Dict(:surplus => FixedProfile(0), :deficit => FixedProfile(1e6)), Dict(Power => 1), - []), + ), ] # Connect all nodes with the availability node for the overall energy/mass balance diff --git a/test/modeltype.jl b/test/modeltype.jl index 62e58ec..aaef18c 100644 --- a/test/modeltype.jl +++ b/test/modeltype.jl @@ -21,7 +21,6 @@ FixedProfile(4), Dict(:surplus => FixedProfile(0), :deficit => FixedProfile(100)), Dict(Power => 1), - [], ) resources = [Power, CO2] diff --git a/test/nodes.jl b/test/nodes.jl index 5914610..ba73d97 100644 --- a/test/nodes.jl +++ b/test/nodes.jl @@ -36,7 +36,6 @@ FixedProfile(10), FixedProfile(0), Dict(Power => 1), - [], ) # Test that an inconsistent Sink.Penalty dict is caught by the checks. @@ -45,7 +44,6 @@ FixedProfile(3), Dict(:surplus => FixedProfile(4), :def => FixedProfile(2)), Dict(Power => 1), - [], ) @test_throws AssertionError simple_graph(source, sink) @@ -56,7 +54,6 @@ FixedProfile(3), Dict(:surplus => FixedProfile(-4), :deficit => FixedProfile(2)), Dict(Power => 1), - [], ) @test_throws AssertionError simple_graph(source, sink) @@ -66,7 +63,6 @@ FixedProfile(3), Dict(:surplus => FixedProfile(-4), :deficit => FixedProfile(4)), Dict(Power => 1), - [], ) m, case, model = simple_graph(source, sink) @test termination_status(m) == MOI.OPTIMAL @@ -81,14 +77,12 @@ FixedProfile(2), FixedProfile(10), Dict(Power => 1.5), - [] ) sink = RefSink( "sink", OperationalProfile([6, 8, 10, 6, 8]), Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(10)), Dict(Power => 1), - [], ) m, case, model = simple_graph(source, sink) @@ -140,14 +134,12 @@ FixedProfile(2), FixedProfile(10), Dict(Power => 1), - [] ) sink = RefSink( "sink", FixedProfile(8), Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(10)), Dict(Power => 1), - [], ) m, case, model = simple_graph(source, sink) @@ -188,7 +180,6 @@ FixedProfile(2), Dict(:surplus => FixedProfile(-100), :deficit => FixedProfile(100)), Dict(Power => 1), - [], ) m, case, model = simple_graph(source, sink) ๐’ฏ = case[:T] @@ -222,14 +213,12 @@ FixedProfile(0), FixedProfile(10), Dict(Power => 1), - [] ) sink = RefSink( "sink", FixedProfile(3), Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(100)), Dict(Power => 1), - [], ) m, case, model = simple_graph(source, sink) ๐’ฏ = case[:T] @@ -294,8 +283,8 @@ end data_source = [EmissionsProcess(Dict(CO2 => 0.5))] else output = Dict(Power => 1) - data_net = [] - data_source = [] + data_net = Vector{Data}([]) + data_source = Vector{Data}([]) end # Used source, network, and sink @@ -322,7 +311,6 @@ end FixedProfile(3), Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(100)), Dict(Power => 1), - [], ) resources = [NG, Power, CO2] @@ -344,7 +332,6 @@ end :deficit => FixedProfile(20), ), Dict(CO2 => 1, Power => 0.02), - [] ) push!(nodes, CO2_stor) append!(links, [Direct(14, source, CO2_stor), Direct(24, network, CO2_stor)]) @@ -651,7 +638,6 @@ end # Function for setting up the system function simple_graph(;ops=SimpleTimes(5, 2), demand=FixedProfile(10)) - # Used source, network, and sink source = RefSource( "source", @@ -659,7 +645,6 @@ end FixedProfile(10), FixedProfile(0), Dict(Power => 1), - [], ) aux_source = RefSource( "aux", @@ -667,7 +652,6 @@ end FixedProfile(10), FixedProfile(0), Dict(aux => 1), - [], ) storage = RefStorage( "storage", @@ -678,7 +662,6 @@ end Power, Dict(Power => 1, aux => 0.05), Dict(Power => 1), - Array{Data}([]) ) sink = RefSink( @@ -686,7 +669,6 @@ end demand, Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(1000)), Dict(Power => 1), - [], ) T = TwoLevel(2, 2, ops; op_per_strat=duration(ops)) @@ -959,7 +941,6 @@ end FixedProfile(10), FixedProfile(0), Dict(NG => 1), - Array{Data}([]) ) network = RefNetworkNode( "network", @@ -979,7 +960,6 @@ end CO2, Dict(CO2 => 1), Dict(CO2 => 1), - Array{Data}([]) ) sink = RefSink( @@ -987,7 +967,6 @@ end FixedProfile(10), Dict(:surplus => FixedProfile(0), :deficit => FixedProfile(10000)), Dict(Power => 1), - [], ) T = TwoLevel(2, 2, ops; op_per_strat=duration(ops)) diff --git a/test/test_utils.jl b/test/test_utils.jl index ed42128..a31695c 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -17,6 +17,12 @@ # `variables_nodes` will continue to the next variable. If this behaviour in JuMP # changes, we must change how variables are created. @test isa(e, ErrorException) + + # Check that the error message is not changed. + pre1 = "An object of name" + pre2 = "is already attached to this model." + @test occursin(pre1, e.msg) + @test occursin(pre2, e.msg) end end @@ -25,16 +31,16 @@ end Power = ResourceCarrier("Power", 0.0) CO2 = ResourceEmit("CO2", 0.0) dict = Dict{Resource,Real}() - array = Array{Resource}([]) + vec = Resource[] source = RefSource("src", FixedProfile(5), FixedProfile(10), FixedProfile(5), - dict, []) - sink = RefSink( "sink", FixedProfile(20), dict, dict, Array{Data}([])) + dict) + sink = RefSink( "sink", FixedProfile(20), Dict{Symbol,TimeProfile}(), dict) stor = RefStorage("stor", FixedProfile(60), FixedProfile(1), FixedProfile(1), - FixedProfile(0), Power, dict, dict, Array{Data}([])) - stor_em = RefStorageEmissions("sink-em", FixedProfile(1), FixedProfile(1), - FixedProfile(1), FixedProfile(1), CO2, dict, dict, Array{Data}([])) - av = GenAvailability("av", array) + FixedProfile(0), Power, dict, dict) + stor_em = RefStorage("stor-em", FixedProfile(1), FixedProfile(1), + FixedProfile(1), FixedProfile(1), CO2, dict, dict) + av = GenAvailability("av", vec) get_types(๐’ฉ) = unique(map(n -> typeof(n), ๐’ฉ))