Update your model to the latest versions
EnergyModelsInvestments is still in a pre-release version. Hence, there are frequently breaking changes occuring, although we plan to keep backwards compatibility. This document is designed to provide users with information regarding how they have to adjust their models to keep compatibility to the latest changes. We will as well implement information regarding the adjustment of extension packages, although this is more difficult due to the vast majority of potential changes.
Adjustments from 0.5.x
The following changes obly affect older models using EnergyModelsBase and EnergyModelsGeography. They are not relevant if you do not use EnergyModelsBase.
Key changes for investment type descriptions
Version 0.7 in EnergyModelsBase introduced the potential to have charge and discharge capacities through storage parameters. This required hence a rework of how we model investments into storage nodes. We decided to use this requirement as an approach for a full rework of the investment options, increasing the potential to make EnergyModelsInvestments independent of EnergyModelsBase.
The key changes are:
- All parameters for investment or lifetime modes are incorporated in the respective investment or lifetime mode.
- The previously used investment data,
InvData,InvDataStorage, andTransInvData, are replaced bySingleInvData(for standard node and transmission mode investments) andStorageInvData(for storage investments). - CAPEX cariables are renamed:
:capex_capis now:cap_capex,:capex_rateis now given by:stor_charge_capexand:stor_discharge_capex,:capex_storis now given by:stor_level_capex, and:capex_transis now given by:trans_cap_capex.
The legacy constructors for calls of the composite type of version 0.5 will be included at least until version 0.7. They are located within the extension from version 0.7 onwards.
InvData and TransInvData
The following changes are written down for InvData, but are equivalent for TransInvData. The previous description for nodal investments was given by:
@kwdef struct InvData <: InvestmentData
capex_cap::TimeProfile
cap_max_inst::TimeProfile
cap_max_add::TimeProfile
cap_min_add::TimeProfile
inv_mode::Investment = ContinuousInvestment()
cap_start::Union{Real, Nothing} = nothing
cap_increment::TimeProfile = FixedProfile(0)
life_mode::LifetimeMode = UnlimitedLife()
lifetime::TimeProfile = FixedProfile(0)
endwhile the new type for single investments is given as
struct SingleInvData <: InvestmentData
cap::AbstractInvData
endwith a constructor allowing directly the creation without having to specify the intermediate type.
The new type, AbstractInvData has at the time being two subtypes, NoStartInvData and StartInvData, given as:
@kwdef struct NoStartInvData <: AbstractInvData
capex::TimeProfile # Capex to install cap
max_inst::TimeProfile # Max installable capacity in one period (in total)
inv_mode::Investment
life_mode::LifetimeMode
end
@kwdef struct StartInvData <: AbstractInvData
capex::TimeProfile # Capex to install cap
max_inst::TimeProfile # Max installable capacity in one period (in total)
initial::Real # Initially installed capacity
inv_mode::Investment
life_mode::LifetimeMode
endHence, the original InvData type would be translated as
# If no starting capacity was provided
SingleInvData(
capex_cap,
cap_max_inst,
tmp_inv_mode,
tmp_life_mode,
)
# If a starting capacity was provided
SingleInvData(
capex_cap,
cap_max_inst,
cap_start,
tmp_inv_mode,
tmp_life_mode,
)
The translation of the parameters cap_max_add, cap_min_add, and cap_increment is dependent on the chosen investment mode, see below. This makes the legacy constructor slightly more complex as it is necessary to check for the individual type.
InvDataStorage
InvDataStorage was significantly reworked since version 0.5. The total rework is provided below.
The previous description for storage investments was given by:
@kwdef struct InvDataStorage <: InvestmentData
capex_rate::TimeProfile
rate_max_inst::TimeProfile
rate_max_add::TimeProfile
rate_min_add::TimeProfile
capex_stor::TimeProfile
stor_max_inst::TimeProfile
stor_max_add::TimeProfile
stor_min_add::TimeProfile
inv_mode::Investment = ContinuousInvestment()
rate_start::Union{Real, Nothing} = nothing
stor_start::Union{Real, Nothing} = nothing
rate_increment::TimeProfile = FixedProfile(0)
stor_increment::TimeProfile = FixedProfile(0)
life_mode::LifetimeMode = UnlimitedLife()
lifetime::TimeProfile = FixedProfile(0)
endThe new storage investment type is now layered, allowing for different options for the individual capacities given through the fields charge, level, and discharge:
@kwdef struct StorageInvData <: InvestmentData
charge::Union{AbstractInvData, Nothing} = nothing
level::Union{AbstractInvData, Nothing} = nothing
discharge::Union{AbstractInvData, Nothing} = nothing
endAs outlined, AbstractInvData has at the time being two subtypes, NoStartInvData and StartInvData. Depending on whether you specified in your previous InvDataStorage instance the field rate_start and stor_start, you will have to use either NoStartInvData (not specified) or StartInvData (specified).
In addition, if you used the rate variables previously for the charge or discharge rate, you have to adjust the investment data slightly. A RefStorage node from EnergyModelsBase would be given then as:
StorageInvData(
charge = NoStartInvData(
capex = capex_rate,
max_inst = rate_max_inst,
inv_mode = inv_mode,
life_mode = life_mode,
),
level = NoStartInvData(
capex = capex_stor,
max_inst = stor_max_inst,
inv_mode = inv_mode,
life_mode = life_mode,
),
)while a node HydroStor from EnergyModelsRenewableProducers, in which the rate was used for the discharge, would be then given as:
StorageInvData(
level = NoStartInvData(
capex = capex_stor,
max_inst = stor_max_inst,
inv_mode = inv_mode,
life_mode = life_mode,
),
discharge = NoStartInvData(
capex = capex_stor,
max_inst = stor_max_inst,
inv_mode = inv_mode,
life_mode = life_mode,
),
)The translation of the parameters rate_max_add, rate_min_add, rate_increment, stor_max_add, stor_min_add, stor_increment is dependent on the chosen investment mode, see below. This makes the legacy constructor slightly more complex as it is necessary to check for the individual type.
Investment modes
Investment modes include now the required data. This implies that the direct translation is now dependent on the individual investment mode. Below, you can find the approach for InvData legacy constructors, although the approach is the same for InvDataStorage and TransInvData.
# DiscreteInvestment
inv_mode = DiscreteInvestment(cap_increment)
# ContinuousInvestment
inv_mode = ContinuousInvestment(cap_min_add, cap_max_add)
# SemiContinuousInvestment
inv_mode = SemiContinuousInvestment(cap_min_add, cap_max_add)
# SemiContinuousOffsetInvestment
inv_mode = SemiContinuousOffsetInvestment(trans_min_add, trans_max_add, capex_trans_offset)We do not provide any constructors for BinaryInvestment and FixedInvestment as it is not possible to deduce the capacity directly from the provided constructor. Instead, the model will throw an error.
BinaryInvestment and FixedInvestment can be solved using the following approach, outlined for a Source node:
# Consider the following Source node in which the investment_data_source used
# BinaryInvestment or FixedInvestment
source = RefSource(
"electricity source", # Node ID
StrategicProfile([10, 15]), # Capacity in MW
FixedProfile(10), # Variable OPEX in EUR/MW
FixedProfile(5), # Fixed OPEX in EUR/year
Dict(Power => 1), # Output from the Node, in this gase, Power
[investment_data_source], # Additional data used for adding the investment data
)
# The original investment data type was given as
investment_data_source = InvData(
capex_cap = FixedProfile(300000), # CAPEX [€/MW]
cap_max_inst = FixedProfile(30), # max installed capacity [MW]
cap_max_add = FixedProfile(30), # max added capactity per sp [MW]
cap_min_add = FixedProfile(0), # min added capactity per sp [MW]
inv_mode = FixedInvestments(), # investment mode
cap_start = 0, # initial capacity
life_mode = RollingLife(), # Lifetime mode
lifetime = FixedProfile(15), # Lifetime
)
# The new investment data type is then given as
investment_data_source = SingleInvData(
FixedProfile(300*1e3), # CAPEX [€/MW]
FixedProfile(30), # max installed capacity [MW]
FixedProfile(0), # max installed capacity [MW]
FixedInvestments(StrategicProfile([10, 15])),
# Line above: Investment mode with the following arguments:
# New capacity in the investment period, if invested in [MW]
RollingLife(FixedProfile(15)), # Lifetime mode
)Lifetime modes
Lifetime modes now include the required lifetime. Below, you can find the approach for InvData legacy constructors, although the approach is the same for InvDataStorage and TransInvData.
# StudyLife
life_mode = StudyLife(lifetime)
# StudyLife
life_mode = PeriodLife(lifetime)
# StudyLife
life_mode = RollingLife(lifetime)