From 1fc89e04fbefe21c2e686eb6c37616e9b6aba4df Mon Sep 17 00:00:00 2001 From: Julian Straus Date: Wed, 27 May 2026 13:03:23 +0200 Subject: [PATCH] Added inner constructor for MultipleBuildingTypes --- docs/src/nodes/multiplebuildingtypes.md | 6 +-- src/datastructures.jl | 49 ++++++++++++++++--------- test/utils.jl | 6 +-- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/docs/src/nodes/multiplebuildingtypes.md b/docs/src/nodes/multiplebuildingtypes.md index 6769963..534e089 100644 --- a/docs/src/nodes/multiplebuildingtypes.md +++ b/docs/src/nodes/multiplebuildingtypes.md @@ -13,7 +13,7 @@ This approach allows modeling building demands with flexible penalty mechanisms The subtype [`MultipleBuildingTypes`](@ref) is used to enable a specialized constructor that samples the [`Tecnalia_Building-Stock-Energy-Model`](https://github.com/iDesignRES/Tecnalia_Building-Stock-Energy-Model) module. -The subtype [`Building`](@ref) can be used to model a simple building demand where the heat demand is calculated from the temperature at a single location and a user-defined temperature-to-demand function. +The subtype [`Building`](@ref) can be used to model a simple building demand where the heat demand is calculated from the temperature at a single location and a user-defined temperature-to-demand function. See the [constructor](@ref lib-pub-sampling_constructors) for more details. !!! note "Sampling Tecnalia_Building-Stock-Energy-Model module" @@ -42,8 +42,8 @@ Standard fields of [AbstractBuildings](@ref EMLI.AbstractBuildings) nodes are gi All values have to be non-negative. - **`data::Vector{<:ExtensionData}`**:\ An entry for providing additional data to the model. - In the current version, it is not applicable. - We intend to change this in future releases to enable investments. + The subtype [`MultipleBuildingTypes`](@ref) adds automatically [`EmissionsEnergy`](@extref EnergyModelsBase.EmissionsEnergy) to account for the emissions given by energy consumption through an inner constructor, if not specified explicitly. + The subtype [`Building`](@ref) does not automatically add [`EmissionsEnergy`](@extref EnergyModelsBase.EmissionsEnergy) as it is not a requirement that the emissions are associated directly to this node. !!! note "Constructor for `AbstractBuildings` nodes" The field `data` is not required as we include a constructor when the value is excluded. diff --git a/src/datastructures.jl b/src/datastructures.jl index 96902ee..5b34432 100644 --- a/src/datastructures.jl +++ b/src/datastructures.jl @@ -2,9 +2,9 @@ abstract type AbstractParameters end """ PVParameters - PVParameters(lat::Real, lon::Real; loss::Real = 14.0, - pvtechchoice::String = "crystSi", mountingplace::String = "free", - optimalangles::Bool = true, usehorizon::Bool = true, + PVParameters(lat::Real, lon::Real; loss::Real = 14.0, + pvtechchoice::String = "crystSi", mountingplace::String = "free", + optimalangles::Bool = true, usehorizon::Bool = true, ) A structure to hold parameters for photovoltaic (PV) power generation. @@ -206,7 +206,7 @@ end PV <: AbstractNonDisRES A photovoltaic source. It extends the existing `AbstractNonDisRES` node through extracting -data from the PVGIS tool from the EU Science Hub (available at https://re.jrc.ec.europa.eu/pvg_tools) +data from the PVGIS tool from the EU Science Hub (available at https://re.jrc.ec.europa.eu/pvg_tools) through a constructor. # Fields @@ -584,7 +584,7 @@ end ) Constructs a [`Building`](@ref) instance where the heat demand profile is generated from temperature data -downloaded using hindcast data (see [`heat_demand_profile`](@ref) for details). +downloaded using hindcast data (see [`heat_demand_profile`](@ref) for details). The temperature-to-demand mapping is provided by `temp_to_demand`. # Arguments @@ -592,7 +592,7 @@ The temperature-to-demand mapping is provided by `temp_to_demand`. - **`cap::Dict{<:Resource,<:TimeProfile}`** is the demand (no need to provide heat demand, it will be generated). - **`penalty_surplus::Dict{<:Resource,<:TimeProfile}`** are the penalties for surplus. - **`penalty_deficit::Dict{<:Resource,<:TimeProfile}`** are the penalties for deficit. -- **`input::Dict{<:Resource,<:Real}`** are the input [`Resource`](@extref EnergyModelsBase.Resource)s +- **`input::Dict{<:Resource,<:Real}`** are the input [`Resource`](@extref EnergyModelsBase.Resource)s with conversion value `Real`. - **`time_start::DateTime`** is the start time for the demand profile. - **`time_end::DateTime`** is the end time for the demand profile. @@ -664,8 +664,9 @@ and deficit. - **`penalty_deficit::Dict{<:Resource,<:TimeProfile}`** are the penalties for deficit. - **`input::Dict{<:Resource,<:Real}`** are the input [`Resource`](@extref EnergyModelsBase.Resource)s with conversion value `Real`. -- **`data::Vector{<:ExtensionData}`** is the additional data (*e.g.*, for investments). The field `data` - is conditional through usage of a constructor. +- **`data::Vector{<:ExtensionData}`** is the additional data. The field `data` is + conditional through usage of a constructor. An inner constructor adds + [`EmissionsEnergy`](@extref EnergyModelsBase.EmissionsEnergy) as data if it is not specified. !!! danger Investments are not available for this node. @@ -677,6 +678,21 @@ struct MultipleBuildingTypes <: AbstractBuildings penalty_deficit::Dict{<:Resource,<:TimeProfile} input::Dict{<:Resource,<:Real} data::Vector{<:ExtensionData} + function MultipleBuildingTypes( + id::Any, + cap::Dict{<:Resource,<:TimeProfile}, + penalty_surplus::Dict{<:Resource,<:TimeProfile}, + penalty_deficit::Dict{<:Resource,<:TimeProfile}, + input::Dict{<:Resource,<:Real}, + data::Vector{<:ExtensionData}, + ) + # Add emission data if it is not included by the user + if !any(isa.(data, EmissionsData)) + data = convert(Vector{ExtensionData}, data) + push!(data, EmissionsEnergy()) + end + new(id, cap, penalty_surplus, penalty_deficit, input, data) + end end function MultipleBuildingTypes( id::Any, @@ -691,7 +707,7 @@ function MultipleBuildingTypes( penalty_surplus, penalty_deficit, input, - ExtensionData[], + ExtensionData[EmissionsEnergy()], ) end @@ -804,8 +820,8 @@ function MultipleBuildingTypes( overwrite_saved_data::Bool = false, ) oper_length = length(T.operational[1]) # Assume the length to be the same in all Strategic periods - resources = values(resources_map) - cap_vec = Dict{Resource,Vector}(resource => zeros(oper_length) for resource ∈ resources) + 𝒫 = values(resources_map) + cap_vec = Dict{Resource,Vector}(resource => zeros(oper_length) for resource ∈ 𝒫) data_path = joinpath(data_location, "all_buildings.yml") if isfile(data_path) && !overwrite_saved_data @@ -850,13 +866,12 @@ function MultipleBuildingTypes( end end - # Convert to OperationalProfile - cap = Dict{Resource,TimeProfile}( - resource => OperationalProfile(cap_vec[resource]) for - resource ∈ resources - ) + # Convert the demand profile to OperationalProfile + cap = Dict{Resource,TimeProfile}(p => OperationalProfile(cap_vec[p]) for p ∈ 𝒫) + + # Create the input map for the node + input = Dict{Resource,Float64}(p => 1.0 for p ∈ 𝒫) - input = Dict{Resource,Real}(resource => 1.0 for resource ∈ resources) return MultipleBuildingTypes(id, cap, penalty_surplus, penalty_deficit, input, data) end diff --git a/test/utils.jl b/test/utils.jl index 9da999d..113b679 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -111,7 +111,9 @@ end function simple_graph_buildings(; cap_p = nothing, penalty_surplus = Dict(HeatHT=>FixedProfile(0.5), Power=>FixedProfile(0.5)), penalty_deficit = Dict(HeatHT=>FixedProfile(0.5), Power=>FixedProfile(0.5)), - input = Dict(HeatHT=>1.0, Power=>1.0)) + input = Dict(HeatHT=>1.0, Power=>1.0), +) + # Creation of the initial problem with the NonDisRES node time_start_str = "2019-01-01" time_end_str = "2019-01-07" @@ -120,7 +122,6 @@ function simple_graph_buildings(; cap_p = nothing, operational_periods = SimpleTimes(op_number, op_duration) sp_duration = [1, 2, 10] - sp_number = length(sp_duration) T = TwoLevel(sp_duration, operational_periods; op_per_strat = 8760.0) # Load paths to default Buildings process @@ -193,7 +194,6 @@ function simple_graph_buildings(; cap_p = nothing, T, # Time structure penalty_surplus, # surplus penalty for the node in €/MWh; penalty_deficit, # deficit penalty for the node in €/MWh; - data = [EmissionsEnergy()], data_location = joinpath(pkgdir(EMLI), "test", "data", "buildings"), overwrite_saved_data = false, )