Technologies

The technology class is a subclass of the ModelComponent class. In the model, we further distinguish between generic technologies and specific technologies. Generic technologies offer a framework that can be adjusted for multiple technologies: only the performance parameters, and the input and output carriers change, while the equations remain the same.

Technology types, divided by generic and specific technologies:

  • Generic technologies:

    • RES

    • CONV1

    • CONV2

    • CONV3

    • CONV4

    • STOR

  • Specific technologies:

    • DAC adsorption

    • Gas turbine

    • Heat pump

    • Hydro open

    • Combined Cycle with fixed size (cannot be sized)

  • Add-ons
    • Carbon Capture

All technology types listed above are modelled as subclasses of the Technology class. An overview of all technologies that are currently modelled, and the technology classes / types used to model them, can be found here.

Additionally, you can attach a post combustion CCS to any technology (see here)

Technology Class

As mentioned, the technology class is a subclass of the ModelComponent class. In general, all technology subclasses share the equations of this class, though some exceptions are there for specific technologies (the subclass then overwrites the class method).

class Technology(tec_data: dict)

Class to read and manage data for technologies

This class is parent class to all generic and specific technologies. It creates the variables, parameters, constraints and sets of a technology.

This function is extented in the generic/specific technology classes. It adds Sets, Parameters, Variables and Constraints that are common for all technologies. The following description is true for new technologies. For existing technologies a few adaptions are made (see below). When CCS is available, we add heat and electricity to the input carriers Set and CO2captured to the output carriers Set. Moreover, we create extra Parameters and Variables equivalent to the ones created for the technology, but specific for CCS. In addition, we create Variables that are the sum of the input, output, CAPEX and OPEX of the technology and of CCS. We calculate the emissions of the technology discounting already what is being captured by the CCS.

Set declarations:

  • set_input_carriers: Set of input carriers

  • set_output_carriers: Set of output carriers

If ccs is possible:

  • set_input_carriers_ccs: Set of CCS input carriers

  • set_output_carriers_ccs: Set of CCS output carriers

** Set declarations for time aggregation:**

Three sets are declared for each technology. These are required for time averaging algorithms:

  • set_t_full: set of all time steps before clustering

  • set_t_performance: set of time steps, on which the technology performance is based on

  • set_t_global: set of time steps, on which the energy balance is based on

Parameter declarations:

The following is a list of declared pyomo parameters.

  • para_size_min: minimal possible size

  • para_size_max: maximal possible size

  • para_unit_capex: investment costs per unit

  • para_unit_capex_annual: Unit CAPEX annualized (annualized from given data on up-front CAPEX, lifetime and discount rate)

  • para_fix_capex: fixed costs independent of size

  • para_fix_capex_annual: Fixed CAPEX annualized (annualized from given data on up-front CAPEX, lifetime and discount rate)

  • para_opex_variable: operational cost EUR/output or input

  • para_opex_fixed: fixed opex as fraction of up-front capex

  • para_tec_emissionfactor: emission factor per output or input

If ccs is possible:

  • para_size_min_ccs: minimal possible size

  • para_size_max_ccs: maximal possible size

  • para_unit_capex_annual_ccs: investment costs per unit (annualized from given data on up-front CAPEX, lifetime and discount rate)

  • para_fix_capex_annual_ccs: Fixed CAPEX annualized (annualized from given data on up-front CAPEX, lifetime and discount rate)

  • para_opex_variable_ccs: operational cost EUR/output or input

  • para_opex_fixed_ccs: fixed opex as fraction of up-front capex

For existing technologies:

  • para_size_initial: initial size

  • para_decommissioning_cost_annual: Decommissioning cost

Variable declarations:

  • var_size: Size of the technology, can be integer or continuous

  • var_input: input to the technology, defined for each input carrier and time slice

  • var_output: output of the technology, defined for each output carrier and time slice

  • var_input_tot: input aggregation of technology and CCS input

  • var_output_tot: output aggregation of technology and CCS output

  • var_capex: annualized investment of the technology

  • var_opex_variable: variable operation costs, defined for each time slice

  • var_opex_fixed: fixed operational costs as fraction of up-front CAPEX

  • var_capex_tot: aggregation of technology and CCS capex

  • var_capex_aux: auxiliary variable to calculate the fixed opex of existing technologies

  • var_opex_variable_tot: aggregation of technology and CCS opex variable, defined for each time slice

  • var_opex_fixed_tot: aggregation of technology and CCS opex fixed

  • var_tec_emissions_pos: positive emissions, defined per time slice

  • var_tec_emissions_neg: negative emissions, defined per time slice

If ccs is possible:

  • var_size_ccs: Size of CCS (in CO2 captured terms)

  • var_input_ccs: input to the CCS component, defined for each CCS input carrier and time slice

  • var_output_ccs: output from the CCS component, defined for each CCS output carrier and time slice

  • var_capex_ccs: annualized investment of CCS

  • var_capex_aux_ccs: auxiliary variable to calculate the fixed opex of existing CCS

  • var_opex_variable_ccs: variable operation costs, defined for each time slice

  • var_opex_fixed_ccs: fixed operational costs

Constraint declarations

  • For new technologies, CAPEX, can be linear (for capex_model == 1), piecewise linear (for capex_model == 2) or linear with a fixed cost when the technology is installed (for capex_model == 3). The capex model can also be overwritten in the children technology classes (for capex_model == 4). Linear is defined as:

    \[capex_{aux} = size * capex_{unitannual}\]

    while linear with fixed installation costs is defined as. Note that capex_aux is zero if the technology is not installed:

    \[capex_{aux} = size * capex_{unitannual} + capex_{fixed}\]

    Existing technologies, i.e. existing = 1, can be decommissioned (decommission = ‘continuous’ or decommission = ‘only_complete’) or not (decommission = ‘impossible’). For technologies that cannot be decommissioned, the size is fixed to the initial size given in the technology data. For technologies that can be decommissioned, the size can be smaller or equal to the initial size. When decommission = ‘continuous’ the size can take any value between the minimum and initial size. When decommission = ‘only_complete’ the size is either 0 or the initial size. Reducing the size comes at the decommissioning costs or benefits specified in the economics of the technology. The fixed opex is calculated by determining the capex that the technology would have costed if newly build and then taking the respective opex_fixed share of this. This is done with the auxiliary variable capex_aux.

  • For existing technologies that can be decommissioned, the CAPEX equal to the decommissioning costs:

    \[capex = (size_{initial} - size) * decommissioningcost\]
  • Variable OPEX: variable opex is defined in terms of the input, with the exception of DAC_Adsorption, RES and CONV4, where it is defined per unit of output:

    \[opexvar_{t} = Input_{t, maincarrier} * opex_{var}\]
  • Fixed OPEX: defined as a fraction of annual CAPEX:

    \[opexfix = capex * opex_{fix}\]
  • Input aggregation: aggregates total input from technology and CCS. In case there is no CCS, input_ccs is zero:

    \[input_{t, car} + input_{CCS, t, car} = input_{tot, t, car}\]
  • Output aggregation: aggregates total output from technology and CCS. In case there is no CCS, output_ccs is zero:

    \[output_{t, car} + output_{CCS, t, car} = output_{tot, t, car}\]
  • Capex aggregation: aggregates capex of technology and CCS. In case there is no CCS, capex_ccs is zero:

    \[capex + capex_{CCS} = capex_{tot}\]
  • Opex variable aggregation: aggregates opex variable of technology and CCS. In case there is no CCS, var_opex_variable_ccs is zero:

    \[opex_{variable, t} + opex_{variable,CCS, t} = opex_{variable,tot, t}\]
  • Opex fixed aggregation: aggregates opex fixed of technology and CCS. In case there is no CCS, opex_fixed_ccs is zero:

    \[opex_{fixed} + opex_{fixed,CCS} = opex_{fixed,tot}\]
  • Emissions: depending if they are based on input or output and depending if emission factor is negative or positive

    \[emissions_{pos/neg,t} = output_{maincarrier, t} * emissionfactor_{pos}\]

If CCS is possible the following constraints apply:

  • Input carriers are given by:

\[input_{CCS, car} <= inputRatio_{carrier} * output_{CCS}/captureRate\]
\[input_{tot, car} = inputTec_{car} + input_{CCS, car}\]
  • CO2 captured output is constrained by:

\[output_{CCS} <= input(output)_{tec} * emissionFactor * captureRate\]
  • The total output are given by:

\[output_{tot, car} = outputTec_{car} + output_{CCS, car}\]
  • Emissions of the technology are:

\[emissions_{tec} = input(output)_{tec} * emissionFactor - output_{CCS}\]
  • CAPEX is given by

\[CAPEX_{CCS} = Size_{CCS} * UnitCost_{CCS} + FixCost_{CCS}\]
\[CAPEX_{tot} = CAPEX_{CCS} + CAPEX_{tec}\]
  • Fixed OPEX: defined as a fraction of annual CAPEX:

\[OPEXfix_{CCS} = CAPEX_{CCS} * opex_{CCS}\]
\[OPEX_{tot} = OPEX_{CCS} + OPEX_{tec}\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Construct the technology model with all required parameters, variable, sets,…

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits technology performance (bounds and coefficients).

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

scale_model(b_tec, model, config)

Scales technology model

Parameters:
  • b_tec – pyomo network block

  • model – pyomo model

  • config (dict) – config dict containing scaling factors

Returns:

pyomo model

write_results_tec_design(h5_group, model_block)

Function to report technology design

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

write_results_tec_operation(h5_group, model_block)

Function to report technology operation

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

Generic Technologies

class Res(tec_data: dict)

Renewable technology with capacity factor (has no input)

Resembles a renewable technology with no input. The capacity factors of the technology are determined for each individual technology type.

So far the following renewable technologies are possible:

  • Photovoltaic (based on irradiance in climate data and PV lib)

  • Wind turbines (based on wind speed and power curves provided)

Constraint declarations:

  • Output of technology. The output can be curtailed in three different ways. For curtailment == 0, there is no curtailment possible. For curtailment == 1, the curtailment is continuous. For curtailment == 2, the size needs to be an integer, and the technology can only be curtailed discretely, i.e. by turning full modules off. For curtailment == 0 (default), it holds:

\[Output_{t, car} = CapFactor_t * Size\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type RES (renewable technology)

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits technology performance

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

write_results_tec_design(h5_group, model_block)

Function to report technology design

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

write_results_tec_operation(h5_group, model_block)

Function to report technology operation

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

class Conv1(tec_data: dict)

Technology with full input an output substitution

This technology type resembles a technology with full input and output substitution, i.e. \(\sum(output) = f(\sum(inputs))\) Three different performance function fits are possible.

Constraint declarations:

  • Size constraints can be formulated on the input or output. For size_based_on == ‘input’ it holds:

    \[\sum(Input_{t, car}) \leq S\]

    For size_based_on == ‘output’ it holds:

    \[\sum(Output_{t, car}) \leq S\]
  • It is possible to limit the maximum input of a carrier. This needs to be specified in the technology JSON files. Then it holds:

    \[Input_{t, car} <= max_in_{car} * \sum(Input_{t, car})\]
  • performance_function_type == 1: Linear through origin. Note that if min_part_load is larger than 0, the technology cannot be turned off.

    \[\sum(Output_{t, car}) == {\alpha}_1 \sum(Input_{t, car})\]
    \[min_part_load * S \leq {\alpha}_1 \sum(Input_{t, car})\]
  • performance_function_type == 2: Linear with minimal partload (makes big-m transformation required). If the technology is in on, it holds:

    \[\sum(Output_{t, car}) = {\alpha}_1 \sum(Input_{t, car}) + {\alpha}_2\]
    \[\sum(Input_{car}) \geq Input_{min} * S\]

    If the technology is off, input and output is set to 0:

    \[\sum(Output_{t, car}) = 0\]
    \[\sum(Input_{t, car}) = 0\]

    If the technology has a standby-power, the input of the standy-by power carrier is:

    \[Input_{t, standby-carrier} = standbypower * S\]
  • performance_function_type == 3: Piecewise linear performance function ( makes big-m transformation required). The same constraints as for performance_function_type == 2 with the exception that the performance function is defined piecewise for the respective number of pieces.

  • performance_function_type == 4:Piece-wise linear, minimal partload. Enables the modeling of technologies with slow (>1h) startup and shutdown trajectories. For more information please refer to dynamics under advanced topics. Based on Equations 9-11, 13 and 15 in Morales-España, G., Ramírez-Elizondo, L., & Hobbs, B. F. (2017). Hidden power system inflexibilities imposed by traditional unit commitment formulations. Applied Energy, 191, 223–238. https://doi.org/10.1016/J.APENERGY.2017.01.089

  • Additionally, ramping rates of the technology can be constrained.

    \[-rampingrate \leq \sum(Input_{t, car}) - \sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type CONV1

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits conversion technology type 1

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

class Conv2(tec_data: dict)

Technology with full input substitution

This technology type resembles a technology with full input substitution, but different performance functions for the respective output carriers, i.e. \(output_{car} = f_{car}(\sum(inputs))\). Three different performance function fits are possible.

Constraint declarations:

  • Size constraints are formulated on the input.

    \[\sum(Input_{t, car}) \leq S\]
  • It is possible to limit the maximum input of a carrier. This needs to be specified in the technology JSON files. Then it holds:

    \[Input_{t, car} <= max_in_{car} * \sum(Input_{t, car})\]
  • performance_function_type == 1: Linear through origin, i.e.:

    \[Output_{t, car} == {\alpha}_{1, car} \sum(Input_{t, car})\]
    \[min_part_load * S \leq {\alpha}_1 \sum(Input_{t, car})\]
  • performance_function_type == 2: Linear with minimal partload (makes big-m transformation required). If the technology is in on, it holds:

    \[Output_{t, car} = {\alpha}_{1, car} \sum(Input_{t, car}) + {\alpha}_{2, car}\]
    \[\sum(Input_{car}) \geq Input_{min} * S\]

    If the technology is off, input and output is set to 0:

    \[Output_{t, car} = 0\]
    \[\sum(Input_{t, car}) = 0\]

    If the technology has a standby-power, the input of the standy-by power carrier is:

    \[Input_{t, standby-carrier} = standbypower * S\]
  • performance_function_type == 3: Piecewise linear performance function ( makes big-m transformation required). The same constraints as for performance_function_type == 2 with the exception that the performance function is defined piecewise for the respective number of pieces.

  • performance_function_type == 4:Piece-wise linear, minimal partload. Enables the modeling of technologies with slow (>1h) startup and shutdown trajectories. For more information please refer to dynamics under advanced topics. Based on Equations 9-11, 13 and 15 in Morales-España, G., Ramírez-Elizondo, L., & Hobbs, B. F. (2017). Hidden power system inflexibilities imposed by traditional unit commitment formulations. Applied Energy, 191, 223–238. https://doi.org/10.1016/J.APENERGY.2017.01.089

  • Additionally, ramping rates of the technology can be constrained.

    \[-rampingrate \leq \sum(Input_{t, car}) - \sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type CONV2

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits conversion technology type 2

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

class Conv3(tec_data: dict)

Technology with no input/output substitution

This technology type resembles a technology for which the output can be written as a function of the input, according to different performance functions that can be specified in the JSON files (performance_function_type). Four different performance function fits of the technology data (again specified in the JSON file) are possible, and for all the function is based on the input of the main carrier , i.e.,: \(output_{car} = f_{car}(input_{maincarrier})\). Note that the ratio between all input carriers is fixed.

Constraint declarations:

For all technologies modelled with CONV3 (regardless of performance function type): - Size constraints are formulated on the input.

\[Input_{t, maincarrier} \leq S\]
  • The ratios of inputs are fixed and given as:

    \[Input_{t, car} = {\phi}_{car} * Input_{t, maincarrier}\]

    If the technology is turned off, all inputs are set to zero.

  • performance_function_type == 1: Linear through origin. Note that if min_part_load is larger 0, the technology cannot be turned off.

    \[Output_{t, car} = {\alpha}_{1, car} Input_{t, maincarrier}\]
    \[min_part_load * S \leq {\alpha}_1 Input_{t, maincarrier}\]
  • performance_function_type == 2: Linear with minimal partload. If the technology is in on, it holds:

    If the technology is in on, it holds:

    \[Output_{t, car} = {\alpha}_{1, car} Input_{t, maincarrier} + {\alpha}_{2, car}\]
    \[Input_{maincarrier} \geq Input_{min} * S\]
  • If the technology is off, input and output are set to 0:

    \[Output_{t, car} = 0\]
    \[Input_{t, maincarrier} = 0\]
  • performance_function_type == 3: Piecewise linear performance function ( makes big-m transformation required). The same constraints as for performance_function_type == 2 with the exception that the performance function is defined piecewise for the respective number of pieces.

  • performance_function_type == 4:Piece-wise linear, minimal partload. Enables the modeling of technologies with slow (>1h) startup and shutdown trajectories. For more information please refer to dynamics under advanced topics. Based on Equations 9-11, 13 and 15 in Morales-España, G., Ramírez-Elizondo, L., & Hobbs, B. F. (2017). Hidden power system inflexibilities imposed by traditional unit commitment formulations. Applied Energy, 191, 223–238. https://doi.org/10.1016/J.APENERGY.2017.01.089

  • Additionally, ramping rates of the technology can be constrained.

    \[-rampingrate \leq Input_{t, main-car} - Input_{t-1, car} \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type CONV3

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits conversion technology type 3

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

class Conv4(tec_data: dict)

Technology with no inputs

This technology type resembles a technology with fixed output ratios and no inputs, i.e., \(output_{car} \leq S\). This technology is useful for modelling a technology for which you do not care about the inputs, i.e., you do not wish to construct and solve an energy balance for the input carriers. Two different performance function fits are possible.

Constraint declarations:

For all performance function types, the following constraints hold:

  • Size constraints are formulated on the output.

    \[Output_{t, maincarrier} \leq S\]
  • The ratios of outputs are fixed and given as:

    \[Output_{t, car} = {\phi}_{car} * Output_{t, maincarrier}\]
  • performance_function_type == 1: No further constraints on the performance of the technology.

  • performance_function_type == 2: A minimum part load can be specified ( requiring a big-m transformation for the solving). The following constraints hold:

    When the technology is on:

    \[Output_{maincarrier} \geq Output_{min} * S\]

    When the technology is off, output is set to 0:

    \[Output_{t, car} = 0\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type CONV4

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits conversion technology type 4

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

class Stor(tec_data: dict)

Storage Technology

This model resembles a storage technology. Note that this technology only works for one carrier, and thus the carrier index is dropped in the below notation.

Variable declarations:

  • var_storage_level: Storage level in \(t\): \(E_t\)

  • var_capacity_charge: Charging capacity

  • var_capacity_discharge: Discharging capacity

Constraint declarations:

The following constants are used:

  • \({\eta}_{in}\): Charging efficiency

  • \({\eta}_{out}\): Discharging efficiency

  • \({\lambda_1}\): Self-Discharging coefficient (independent of environment)

  • \({\lambda_2(\Theta)}\): Self-Discharging coefficient (dependent on environment)

  • \(Input_{max}\): Maximal charging capacity

  • \(Output_{max}\): Maximal discharging capacity

  • Size constraint:

    \[E_{t} \leq S\]
  • Maximal charging and discharging:

    \[Input_{t} \leq Input_{max}\]
    \[Output_{t} \leq Output_{max}\]
  • Storage level calculation:

    \[E_{t} = E_{t-1} * (1 - \lambda_1) - \lambda_2(\Theta) * E_{t-1} + {\eta}_{in} * Input_{t} - 1 / {\eta}_{out} * Output_{t}\]
  • If allow_only_one_direction == 1, simultaneous charging and discharging is limited using Equation 24 from Morales-España, Germán & Hernandez, Ricardo & Helistö, Niina & Kiviluoma, Juha. (2022). LP Formulation for Optimal Investment and Operation of Storage Including Reserves. 10.13140/RG.2.2.27048.03840.

  • If allow_only_one_direction_precise == 1, then only input or output can be unequal to zero in each respective time step (otherwise, simultaneous charging and discharging can lead to unwanted ‘waste’ of energy/material).

  • If in Flexibility the power_energy_ratio == fixed, then the capacity of the charging and discharging power is fixed as a ratio of the energy capacity. Thus:

    \[Input_{max} = \gamma_{charging} * S\]
  • If in ‘Flexibility’ the “power_energy_ratio == flexratio”, then the capacity of the charging and discharging power is a variable in the optimization. In this case, the charging and discharging rates specified in the json file are the maximum installed capacities as a ratio of the energy capacity. The model will optimize the charging and discharging capacities, based on the incorporation of these components in the CAPEX function.

  • If in ‘Flexibility’ the “power_energy_ratio == fixedratio”, then the capacity of the charging and discharging power is a fraction of the installed capacity. In this case, the charging and discharging rates specified in the json file are a ratio of the energy capacity.

  • If in ‘Flexibility’ the “power_energy_ratio == fixedcapacity”, then the capacity of the charging and discharging power is a fixed input parameter. In this case, the charging and discharging rates specified in the json file in the same unit as the input and output.

  • If an energy consumption for charging or dis-charging process is given, the respective carrier input is:

    \[Input_{t, car} = cons_{car, in} Input_{t}\]
    \[Input_{t, car} = cons_{car, out} Output_{t}\]
  • CAPEX is given by three contributions, the CAPEX of the charging capacity, the CAPEX of the discharging capacity, and the CAPEX of the energy capacity (i.e., the size of the storage).

    \[CAPEX_{chargeCapacity} = chargeCapacity * unitCost_{chargeCapacity}\]
    \[CAPEX_{dischargeCapacity} = dischargeCapacity * unitCost_{dischargeCapacity}\]
    \[CAPEX_{storSize} = storSize * unitCost_{storSize}\]
  • Additionally, ramping rates of the technology can be constraint (for input and output).

    \[-rampingrate \leq Input_{t, maincar} - Input_{t-1, maincar} \leq rampingrate\]
    \[-rampingrate \leq \sum(Output_{t, car}) - \sum(Output_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type STOR, resembling a storage technology

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits conversion technology type STOR and fills in the fitted parameters in a dict

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

write_results_tec_design(h5_group, model_block)

Function to report technology design

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

write_results_tec_operation(h5_group, model_block)

Function to report technology operation

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

Specific Technologies

Solid Sorbent Direct Air Capture

class DacAdsorption(tec_data: dict)

Direct Air Capture technology (adsorption)

The model resembles as Direct Air Capture technology with a modular setup. It has a heat and electricity input and CO2 as an output. The performance is based on data for a generic solid sorbent, as described in the article (see below). The performance data is fitted to the ambient temperature and humidity at the respective node.

The model is based on Wiegner et al. (2022). Optimal Design and Operation of Solid Sorbent Direct Air Capture Processes at Varying Ambient Conditions. Industrial and Engineering Chemistry Research, 2022, 12649–12667. https://doi.org/10.1021/acs.iecr.2c00681. It resembles operation configuration 1 without water spraying.

construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type DAC_adsorption

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits the technology performance

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

write_results_tec_operation(h5_group, model_block)

Function to report technology operation

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

Heat Pump

class HeatPump(tec_data: dict)

Heat pump

Resembles a heat pump Three different types of heat pumps are possible: air sourced ( ‘HeatPump_AirSourced’), ground sourced (‘HeatPump_GroundSourced’) and water sourced (‘HeatPump_WaterSourced’). Additionally, a heating curve is determined for heating for buildings. Then, the application needs to be set to either ‘floor_heating’ or ‘radiator_heating’ in the data file. Otherwise, the output temperature of the heat pump can also be set to a given temperature. The coefficient of performance at full load is calculated in the respective fitting function with the equations provided in Ruhnau, O., Hirth, L., & Praktiknjo, A. (2019). Time series of heat demand and heat pump efficiency for energy system modeling. Scientific Data, 6(1). https://doi.org/10.1038/s41597-019-0199-y

The part load behavior is modelled after equation (3) in Xu, Z., Li, H., Xu, W., Shao, S., Wang, Z., Gou, X., Zhao, M., & Li, J. (2022). Investigation on the efficiency degradation characterization of low ambient temperature air source heat pump under partial load operation. International Journal of Refrigeration, 133, 99–110. https://doi.org/10.1016/J.IJREFRIG.2021.10.002

Essentially, the equations for the heat pump model are the same as for generic conversion technology type 1 (with time-dependent performance parameter).

Ramping rates of the technology can be constraint.

\[-rampingrate \leq \sum(Input_{t, car}) - \sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type HP (Heat Pump)

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Performs fitting for technology type HeatPump

Parameters:
  • tec_data – technology data

  • climate_data – climate data

Returns:

Gas Turbine

class GasTurbine(tec_data: dict)

Gas turbine

Resembles gas turbines of different sizes. Hydrogen and Natural Gas Turbines are possible at four different sizes, as indicated by the file names of the data. Performance data and the model is taken from Weimann, L., Ellerker, M., Kramer, G. J., & Gazzani, M. (2019). Modeling gas turbines in multi-energy systems: A linear model accounting for part-load operation, fuel, temperature, and sizing effects. International Conference on Applied Energy. https://doi.org/10.46855/energy-proceedings-5280

A small adaption is made: Natural gas turbines can co-fire hydrogen up to 5% of the energy content

Variable declarations:

  • Total fuel input in \(t\): \(Input_{tot, t}\)

  • Number of turbines on in \(t\): \(N_{on,t}\)

Constraint declarations:

The following constants are used:

  • \(Input_{min}\): Minimal input per turbine

  • \(Input_{max}\): Maximal input per turbine

  • \(in_{H2max}\): Maximal H2 admixture to fuel (only for natural gas turbines, default is 0.05)

  • \({\alpha}\): Performance parameter for electricity output

  • \({\beta}\): Performance parameter for electricity output

  • \({\epsilon}\): Performance parameter for heat output

  • \(f({\Theta})\): Ambient temperature correction factor

  • Input calculation (For hydrogen turbines, \(Input_{NG, t}\) is zero, and the second constraint is removed):

    \[Input_{H2, t} + Input_{NG, t} = Input_{tot, t}\]
    \[Input_{H2, t} \leq in_{H2max} Input_{tot, t}\]
  • Turbines on:

    \[N_{on, t} \leq S\]
  • If technology is on:

    \[Output_{el,t} = ({\alpha} Input_{tot, t} + {\beta} * N_{on, t}) *f({\Theta})\]
    \[Output_{th,t} = {\epsilon} Input_{tot, t} - Output_{el,t}\]
    \[Input_{min} * N_{on, t} \leq Input_{tot, t} \leq Input_{max} * N_{on, t}\]
  • If the technology is off, input and output is set to 0:

    \[\sum(Output_{t, car}) = 0\]
    \[\sum(Input_{t, car}) = 0\]
  • Additionally, ramping rates of the technology can be constraint.

    \[-rampingrate \leq \sum(Input_{t, car}) - \sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for gas turbines

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Performs fitting for technology type GasTurbine

Parameters:
  • tec_data – technology data

  • climate_data – climate data

Returns:

write_results_tec_operation(h5_group, model_block)

Function to report results of technologies after optimization

Parameters:

b_tec – technology model block

Returns:

dict results: holds results

Hydro Open

class HydroOpen(tec_data: dict)

Open pumped hydro technology

Resembles a pumped hydro plant with additional natural inflows (defined in climate data). Note that this technology only works for one carrier, and thus the carrier index is dropped in the below notation.

Variable declarations:

  • Storage level in \(t\): \(E_t\)

  • Charging in \(t\): \(Input_{t}\)

  • Discharging in \(t\): \(Output_{t}\)

Constraint declarations:

The following constants are used:

  • \({\eta}_{in}\): Charging efficiency

  • \({\eta}_{out}\): Discharging efficiency

  • \({\lambda}\): Self-Discharging coefficient

  • \(Input_{max}\): Maximal charging capacity in one time-slice

  • \(Output_{max}\): Maximal discharging capacity in one time-slice

  • \(Natural_Inflow{t}\): Natural water inflow in time slice (can be negative, i.e. being an outflow)

  • Maximal charging and discharging:

    \[Input_{t} \leq Input_{max}\]
    \[Output_{t} \leq Output_{max}\]
  • Size constraint:

    \[E_{t} \leq S\]
  • Storage level calculation:

    \[E_{t} = E_{t-1} * (1 - \lambda) + {\eta}_{in} * Input_{t} - 1 / {\eta}_{out} * Output_{t} + Natural_Inflow_{t}\]
  • If allow_only_one_direction == 1, then only input or output can be unequal to zero in each respective time step (otherwise, simultanous charging and discharging can lead to unwanted ‘waste’ of energy/material).

  • Additionally, ramping rates of the technology can be constraint (for input and output).

    \[-rampingrate \leq Input_{t, maincar} - Input_{t-1, maincar} \leq rampingrate\]
    \[-rampingrate \leq \sum(Input_{t, car}) - \sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for tec_type Hydro_Open

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Fits technology performance

Parameters:
  • climate_data (pd.Dataframe) – dataframe containing climate data

  • location (dict) – dict containing location details

write_results_tec_operation(h5_group, model_block)

Function to report technology operation

Parameters:
  • model_block – pyomo network block

  • h5_group – h5 group to write to

Combined Cycle with fixed size

class CCPP(tec_data: dict)

Combined Cycle Power Plant with Steam Production

Resembles Combined Cycle Power Plant with Steam Production. The size of the power plant it fixed and it only possible to model the plant as an existing technology (i.e. it cannot be sized). The model is based on Wiegner et al. (2024). Optimizing the Use of Limited Amounts of Hydrogen in Existing Combined Heat and Power Plants https://www.sciencedirect.com/science/article/pii/S2667095X24000199.

It is possible in the JSON file to specify the following options:

  • size_ohb: Size of an Oxy-fuel hydrogen burner

  • size_db: Size of a duct burner that can burn hydrogen or natural gas

  • max_h2_in_gt: Max share (energy-based) of hydrogen combustion in the gas turbine (“max_input”)

  • max_h2_in: Maximum of hydrogen input to plant

  • How to treat steam production (“steam_production”):

    • “ignore”: technology has no additional output other than electricity

    • “as heat”: technology has heat output, equivalent to the sum of HP/MP generation

    • “as steam”: technology has steam output, equivalent to the sum of HP/MP generation

    • “as hp/mp steam”: technology has HP and MP steam output

  • What additional components in the plant should be considered. Possible options are:

    • “None”: only CCPP

    • “DB”: Duct burner

    • “OHB”: Oxyfuel-hydrogen burner

  • Number of segments to use for performance function (“nr_segments”). Recommended: 2

Variable declarations:

  • Total fuel input in \(t\): \(Input_{tot, t}\)

  • Fuel input to duct burner/ OHB \(Input_{OHB/DB, t}\)

Constraint declarations:

The following constants are used:

  • \(Input_{min}\): Minimal input per turbine

  • \(Input_{max}\): Maximal input per turbine

  • \(in_{H2max}\): Maximal H2 admixture to fuel (only for natural gas turbines, default is 0.05)

  • \({\alpha}\): Performance parameter for electricity output

  • \({\beta}\): Performance parameter for electricity output

  • \({\epsilon}\): Performance parameter for heat output

  • \(f({\Theta})\): Ambient temperature correction factor

  • Input calculation (For hydrogen turbines, \(Input_{NG, t}\) is zero, and the second constraint is removed):

    \[Input_{H2, t} + Input_{NG, t} = Input_{tot, t}\]
    \[Input_{H2, t} \leq in_{H2max} Input_{tot, t}\]
  • Turbines on:

    \[N_{on, t} \leq S\]
  • If technology is on:

    \[Output_{el,t} = ({\alpha} Input_{tot, t} + {\beta} * N_{on, t}) *f({\Theta})\]
    \[Output_{th,t} = {\epsilon} Input_{tot, t} - Output_{el,t}\]
    \[Input_{min} * N_{on, t} \leq Input_{tot, t} \leq Input_{max} * N_{on, t}\]
  • If the technology is off, input and output is set to 0:

    \[\sum(Output_{t, car}) = 0\]
    \[\sum(Input_{t, car}) = 0\]
  • Additionally, ramping rates of the technology can be constraint.

    \[-rampingrate \leq\sum(Input_{t, car}) -\sum(Input_{t-1, car}) \leq rampingrate\]
construct_tech_model(b_tec, data: dict, set_t_full, set_t_clustered)

Adds constraints to technology blocks for gas turbines

Parameters:
  • b_tec – pyomo block with technology model

  • data (dict) – data containing model configuration

  • set_t_full – pyomo set containing timesteps

  • set_t_clustered – pyomo set containing clustered timesteps

Returns:

pyomo block with technology model

fit_technology_performance(climate_data: DataFrame, location: dict)

Performs fitting for technology type CCPP

Parameters:
  • tec_data – technology data

  • climate_data – climate data

Returns:

write_results_tec_operation(h5_group, model_block)

Function to report results of technologies after optimization

Parameters:

b_tec – technology model block

Returns:

dict results: holds results

Carbon Capture

The carbon capture object (CCS, even though it refers just to the capture technology), which does not constitute an independent technology itself, can be attached to any technology with a positive emission factor. To do this, you need to add (if not already present) the following lines of code to the json file under the “Performance” section of the technology you wish to equip with CCS:

“ccs”: {

“possible”: 1, “co2_concentration” : 0.08, “ccs_type”: “MEA_medium”

},

To see an example of how this is done, you can look at the json file of the GasTurbine_simple_CCS technology. When you want to have the possibility of installing CCS, you need to set the “possible” option to 1. Moreover, you can specify the CO2 concentration in the flue gas of your emitting technology; this will influence the costs and energy performance of the CCS. With “ccs_type” you can specify the specific capture technology you wish to use. So far, only post combustion capture with MEA is modelled (following the work of Weimann et Al. 2023 https://doi.org/10.1016/j.apenergy.2023.120738), and you can choose the between small, medium and large according to the size range that you expect for the capture plant (the range of the sizes – based on the flue gas flow in t/h – can be found in each json file of the CCS object, e.g. “MEA_medium.json”).

The CCS needs heat and electricity as input to run. Therefore, you have to make sure to have those carriers available. Moreover, the CCS will produce an extra carrier as output of the emitting technology called “CO2captured” in amount equal to the CO2 captured from the flue gas. The CO2captured needs to be sent to a storage site or exported. In AdOpT-NET0 a storage site is represented with the SINK class of technologies. So far, only the “PermanentStorage_CO2_simple” SINK technology is available. This storage takes CO2captured and electricity (for compression and injection) as inputs and it has no output. The costs of this sink are given only by the amount of CO2 stored times a fixed cost per ton of CO2 (no capital costs).

To account for the capture of CO2 in the emissions balance, when the CC is implemented the amount of CO2 that is captured is subtracted from the emissions of the technology.

To summarize, if you want to add the CCS option to a technology you have to: - Add “ccs” section in the “Performance” of the json file as above and adjust the settings to your case study (concentration and CC technology type and size) - Have electricity, heat (with import possibility if required) and CO2captured as carriers - Have a technology type SINK or CO2captured export option - Add CO2 transport option if sink and capture are in different nodes