Commit 20214e54 authored by Niina Helistö's avatar Niina Helistö
Browse files

Merge branch 'dev' into LossOfLargestInterconnector

parents 4ec02a9b 39a6bfc3
...@@ -26,12 +26,11 @@ Created by: ...@@ -26,12 +26,11 @@ Created by:
Ran Li Ran Li
Ciara O'Dwyer Ciara O'Dwyer
- Based on Stochastic Model Predictive Control method [1]. This is a GAMS implementation of the Backbone energy system modelling framework
- Enables multiple different models (m) to be implemented by changing [1]. Features include:
the temporal structure of the model. (MULTI-MODEL RUNS TO BE IMPLEMENTED) - Based on Stochastic Model Predictive Control method [2].
- Time steps (t) can vary in length. - Time steps (t) can vary in length.
- Short term forecast stochasticity (f) and longer term statistical uncertainty (s). - Short term forecast stochasticity (f) and longer term statistical uncertainty (s).
- Can handle ramp based dispatch in addition to energy blocks. (TO BE IMPLEMENTED)
GAMS command line arguments GAMS command line arguments
...@@ -63,7 +62,10 @@ GAMS command line arguments ...@@ -63,7 +62,10 @@ GAMS command line arguments
References References
---------- ----------
[1] K. Nolde, M. Uhr, and M. Morari, Medium term scheduling of a hydro-thermal [1] N. Helist et al., Backbone---An Adaptable Energy Systems Modelling Framework,
Energies, vol. 12, no. 17, p. 3388, Sep. 2019. Available at:
https://dx.doi.org/10.3390/en12173388.
[2] K. Nolde, M. Uhr, and M. Morari, Medium term scheduling of a hydro-thermal
system using stochastic model predictive control, Automatica, vol. 44, system using stochastic model predictive control, Automatica, vol. 44,
no. 6, pp. 15851594, Jun. 2008. no. 6, pp. 15851594, Jun. 2008.
...@@ -126,7 +128,7 @@ $include '%input_dir%/modelsInit.gms' ...@@ -126,7 +128,7 @@ $include '%input_dir%/modelsInit.gms'
* === Simulation ============================================================== * === Simulation ==============================================================
$include 'inc/3a_periodicInit.gms' // Initialize modelling loop $include 'inc/3a_periodicInit.gms' // Initialize modelling loop
loop(modelSolves(mSolve, tSolve), loop(modelSolves(mSolve, tSolve)$(execError = 0),
solveCount = solveCount + 1; solveCount = solveCount + 1;
$$include 'inc/3b_periodicLoop.gms' // Update modelling loop $$include 'inc/3b_periodicLoop.gms' // Update modelling loop
$$include 'inc/3c_inputsLoop.gms' // Read input data that is updated within the loop $$include 'inc/3c_inputsLoop.gms' // Read input data that is updated within the loop
...@@ -143,7 +145,6 @@ $ifthene.debug %debug%>1 ...@@ -143,7 +145,6 @@ $ifthene.debug %debug%>1
$$include defOutput/debugSymbols.inc $$include defOutput/debugSymbols.inc
; ;
$endif.debug $endif.debug
if(execError, put log "!!! Errors encountered: " execError:0:0);
); );
$if exist '%input_dir%/3z_modelsClose.gms' $include '%input_dir%/3z_modelsClose.gms'; $if exist '%input_dir%/3z_modelsClose.gms' $include '%input_dir%/3z_modelsClose.gms';
...@@ -164,6 +165,8 @@ execute_unload '%output_dir%/results.gdx', ...@@ -164,6 +165,8 @@ execute_unload '%output_dir%/results.gdx',
$ife %debug%>0 $ife %debug%>0
execute_unload '%output_dir%/debug.gdx'; execute_unload '%output_dir%/debug.gdx';
if(execError,
if(errorcount > 0, abort errorcount); putclose log "!!! Errors encountered: " execError:0:0/;
abort "FAILED";
);
* === THE END ================================================================= * === THE END =================================================================
...@@ -7,12 +7,15 @@ All notable changes to this project will be documented in this file. ...@@ -7,12 +7,15 @@ All notable changes to this project will be documented in this file.
- All input files, including *inputData.gdx*, are optional - All input files, including *inputData.gdx*, are optional
- Enabling different combinations of LP and MIP online and invest variables - Enabling different combinations of LP and MIP online and invest variables
- Separate availability parameter for output units in the capacity margin constraint - Separate availability parameter for output units in the capacity margin constraint
- Parameter `gn_forecasts(*, node, timeseries)` to tell which nodes and timeseries use forecasts
### Changed ### Changed
- Updated tool defintions for Sceleton Titan and Spine Toolbox - Updated tool defintions for Sceleton Titan and Spine Toolbox
- The program will now stop looping in case of execution errors.
### Fixed ### Fixed
- Removed hard-coded `elec grids` from *setVariableLimits* and *rampSched files* - Removed hard-coded `elec grids` from *setVariableLimits* and *rampSched files*
- Time series smooting not working at all (#100)
## [1.1] - 2019-04-17 ## [1.1] - 2019-04-17
### Added ### Added
......
# Backbone # Backbone
Backbone is a generic energy network optimization tool written in [GAMS](https://www.gams.com/). It has been designed to be highly adaptable in different dimensions: temporal, spatial, technology representation and market design. The model can represent stochastics with a model predictive control method [1], with short-term forecasts and longer-term statistical uncertainties. Backbone can support multiple different models due to the modifiable temporal structure and varying lengths of the time steps. Backbone is a generic energy network optimization tool written in [GAMS](https://www.gams.com/). It has been designed to be highly adaptable in different dimensions: temporal, spatial, technology representation and market design. The model can represent stochastics with a [model predictive control method](https://doi.org/10.1016/j.automatica.2008.03.002), with short-term forecasts and longer-term statistical uncertainties. Backbone can support multiple different models due to the modifiable temporal structure and varying lengths of the time steps.
[1] Nolde, K., Uhr, M., & Morari, M. (2008). Medium term scheduling of a hydro-thermal system using stochastic model predictive control. Automatica, 1585-1594. If you use Backbone in a published work, please cite the [following publication](https://doi.org/10.3390/en12173388), which describes the Backbone energy systems modelling framework.
## Getting Started ## Getting Started
...@@ -10,7 +10,9 @@ Make sure that you have [Git](https://git-scm.com/) version control system and a ...@@ -10,7 +10,9 @@ Make sure that you have [Git](https://git-scm.com/) version control system and a
In order to get a copy of the Backbone project, you need to clone it using Git. Copy and paste the URL of the original Backbone repository and select the directory where you want Backbone to be cloned. The URL of the original Backbone repository is https://gitlab.vtt.fi/backbone/backbone. In order to get a copy of the Backbone project, you need to clone it using Git. Copy and paste the URL of the original Backbone repository and select the directory where you want Backbone to be cloned. The URL of the original Backbone repository is https://gitlab.vtt.fi/backbone/backbone.
You should now have *Backbone.gms*, a few additional files and three subdirectories in the directory where you cloned Backbone. Note that you need to manually create two additional subdirectories in order to get Backbone working. These subdirectories should be named *input* and *output* and they should be created in the same directory where *Backbone.gms* is. You should now have *Backbone.gms*, a few additional files and five subdirectories in the directory where you cloned Backbone.
Small example input datasets are provided online in the [wiki](https://gitlab.vtt.fi/backbone/backbone/wikis/Example-data-sets).
## Model File Structure ## Model File Structure
...@@ -29,8 +31,8 @@ Backbone has been designed with a modular structure, making it easier to change ...@@ -29,8 +31,8 @@ Backbone has been designed with a modular structure, making it easier to change
* 2d_constraints.gms - Contains definitions for constraint equations. * 2d_constraints.gms - Contains definitions for constraint equations.
* *Model Definition Files* - Contains GAMS definitions for different models, essentially lists the equations (constraints) that apply. Current files include *schedule.gms*, *building.gms* and *invest.gms*. * *Model Definition Files* - Contains GAMS definitions for different models, essentially lists the equations (constraints) that apply. Current files include *schedule.gms*, *building.gms* and *invest.gms*.
* 3a_periodicInit.gms - Initializes various data and sets for the solve loop. * 3a_periodicInit.gms - Initializes various data and sets for the solve loop.
* 3b_inputsLoop.gms - Instructions for possible data import inside the solve loop, as well as forecast in-the-loop improvements. * 3b_periodicLoop.gms - Contains instructions for the forecast-interval structure of the desired model.
* 3c_periodicLoop.gms - Contains instructions for the forecast-time structure of the desired model. * 3c_inputsLoop.gms - Contains instructions for updating the forecast data, optional forecast improvements, aggregating time series data for the time intervals, and other input data processing.
* 3d_setVariableLimits.gms - Defines the variable boundaries for each solve. * 3d_setVariableLimits.gms - Defines the variable boundaries for each solve.
* 3e_solve.gms - Contains the GAMS solve command for using the solver. * 3e_solve.gms - Contains the GAMS solve command for using the solver.
* 3f_afterSolve.gms - Fixes some variable values after solve. * 3f_afterSolve.gms - Fixes some variable values after solve.
...@@ -45,7 +47,7 @@ Most of these files are under *\inc* in the Backbone folder, except for the mode ...@@ -45,7 +47,7 @@ Most of these files are under *\inc* in the Backbone folder, except for the mode
* timeAndSamples.inc - Contains definitions for the time, forecast and sample index ranges. * timeAndSamples.inc - Contains definitions for the time, forecast and sample index ranges.
* modelsInit.gms - Contains model parameters for the solve (or a link to a template under *\defModels* to be used). Useful for any additional GAMS scripting. * modelsInit.gms - Contains model parameters for the solve (or a link to a template under *\defModels* to be used). Useful for any additional GAMS scripting.
Backbone folder contains three template files *1_options_temp.gms*, *timeAndSamples_temp.inc*, and *modelsInit_temp.gms* to provide examples of the input format. These files can be copied into *\input* and renamed to *1_options.gms*, *timeAndSamples.inc*, and *modelsInit.gms*. Backbone folder contains template files *1_options_temp.gms*, *timeAndSamples_temp.inc*, and *modelsInit_temp.gms* to provide examples of the input format. These files can be copied into *\input* and renamed to *1_options.gms*, *timeAndSamples.inc*, and *modelsInit.gms*.
## When Simply Using Backbone ## When Simply Using Backbone
...@@ -62,6 +64,9 @@ When starting to use Backbone, there is no immediate need to understand every si ...@@ -62,6 +64,9 @@ When starting to use Backbone, there is no immediate need to understand every si
* Erkka Rinne * Erkka Rinne
* Topi Rasku * Topi Rasku
* Niina Helisto * Niina Helisto
* Dana Kirchem
* Ran Li
* Ciara O'Dwyer
## License ## License
......
...@@ -27,8 +27,11 @@ Model building / ...@@ -27,8 +27,11 @@ Model building /
// Unit Operation // Unit Operation
q_maxDownward q_maxDownward
* q_maxDownwardOfflineReserve
q_maxUpward q_maxUpward
* q_maxUpwardOfflineReserve
* q_reserveProvision * q_reserveProvision
* q_reserveProvisionOnline
* q_startup * q_startup
* q_startuptype * q_startuptype
* q_onlineLimit * q_onlineLimit
......
...@@ -54,6 +54,8 @@ if (mType('building'), ...@@ -54,6 +54,8 @@ if (mType('building'),
// Define the probability (weight) of samples // Define the probability (weight) of samples
p_msProbability('building', s) = 0; p_msProbability('building', s) = 0;
p_msProbability('building', 's000') = 1; p_msProbability('building', 's000') = 1;
p_msWeight('building', s) = 0;
p_msWeight('building', 's000') = 1;
* --- Define Time Step Intervals ---------------------------------------------- * --- Define Time Step Intervals ----------------------------------------------
......
...@@ -27,8 +27,11 @@ Model invest / ...@@ -27,8 +27,11 @@ Model invest /
// Unit Operation // Unit Operation
q_maxDownward q_maxDownward
q_maxDownwardOfflineReserve
q_maxUpward q_maxUpward
q_maxUpwardOfflineReserve
q_reserveProvision q_reserveProvision
q_reserveProvisionOnline
q_startshut q_startshut
q_startuptype q_startuptype
q_onlineOnStartUp q_onlineOnStartUp
......
...@@ -58,9 +58,13 @@ if (mType('invest'), ...@@ -58,9 +58,13 @@ if (mType('invest'),
// Define the probability (weight) of samples // Define the probability (weight) of samples
p_msProbability('invest', s) = 0; p_msProbability('invest', s) = 0;
p_msProbability('invest', 's000') = 8760/504; p_msProbability('invest', 's000') = 1;
p_msProbability('invest', 's001') = 8760/504; p_msProbability('invest', 's001') = 1;
p_msProbability('invest', 's002') = 8760/504; p_msProbability('invest', 's002') = 1;
p_msWeight('invest', s) = 0;
p_msWeight('invest', 's000') = 8760/504;
p_msWeight('invest', 's001') = 8760/504;
p_msWeight('invest', 's002') = 8760/504;
* --- Define Time Step Intervals ---------------------------------------------- * --- Define Time Step Intervals ----------------------------------------------
...@@ -78,6 +82,10 @@ if (mType('invest'), ...@@ -78,6 +82,10 @@ if (mType('invest'),
// Define the number of forecasts used by the model // Define the number of forecasts used by the model
mSettings('invest', 'forecasts') = 0; mSettings('invest', 'forecasts') = 0;
// Define which nodes and timeseries use forecasts
//Option clear = gn_forecasts; // By default includes everything, so clear first
//gn_forecasts('wind', 'XXX', 'ts_cf') = yes;
// Define forecast properties and features // Define forecast properties and features
mSettings('invest', 't_forecastStart') = 0; // At which time step the first forecast is available ( 1 = t000001 ) mSettings('invest', 't_forecastStart') = 0; // At which time step the first forecast is available ( 1 = t000001 )
mSettings('invest', 't_forecastLengthUnchanging') = 0; // Length of forecasts in time steps - this does not decrease when the solve moves forward (requires forecast data that is longer than the horizon at first) mSettings('invest', 't_forecastLengthUnchanging') = 0; // Length of forecasts in time steps - this does not decrease when the solve moves forward (requires forecast data that is longer than the horizon at first)
......
...@@ -31,8 +31,11 @@ Model schedule / ...@@ -31,8 +31,11 @@ Model schedule /
q_resDemandLargestInfeedTransfer q_resDemandLargestInfeedTransfer
// Unit Operation // Unit Operation
q_maxDownward q_maxDownward
q_maxDownwardOfflineReserve
q_maxUpward q_maxUpward
q_maxUpwardOfflineReserve
* q_reserveProvision * q_reserveProvision
q_reserveProvisionOnline
q_startshut q_startshut
q_startuptype q_startuptype
q_onlineLimit q_onlineLimit
......
...@@ -64,6 +64,8 @@ if (mType('schedule'), ...@@ -64,6 +64,8 @@ if (mType('schedule'),
// Define the probability (weight) of samples // Define the probability (weight) of samples
p_msProbability('schedule', s) = 0; p_msProbability('schedule', s) = 0;
p_msProbability('schedule', 's000') = 1; p_msProbability('schedule', 's000') = 1;
p_msWeight('schedule', s) = 0;
p_msWeight('schedule', 's000') = 1;
// If using long-term samples, uncomment // If using long-term samples, uncomment
//ms_central('schedule', 's001') = yes; //ms_central('schedule', 's001') = yes;
...@@ -104,6 +106,10 @@ if (mType('schedule'), ...@@ -104,6 +106,10 @@ if (mType('schedule'),
// Define the number of forecasts used by the model // Define the number of forecasts used by the model
mSettings('schedule', 'forecasts') = 3; mSettings('schedule', 'forecasts') = 3;
// Define which nodes and timeseries use forecasts
//Option clear = gn_forecasts; // By default includes everything, so clear first
//gn_forecasts('wind', 'XXX', 'ts_cf') = yes;
// Define forecast properties and features // Define forecast properties and features
mSettings('schedule', 't_forecastStart') = 1; // At which time step the first forecast is available ( 1 = t000001 ) mSettings('schedule', 't_forecastStart') = 1; // At which time step the first forecast is available ( 1 = t000001 )
mSettings('schedule', 't_forecastLengthUnchanging') = 36; // Length of forecasts in time steps - this does not decrease when the solve moves forward (requires forecast data that is longer than the horizon at first) mSettings('schedule', 't_forecastLengthUnchanging') = 36; // Length of forecasts in time steps - this does not decrease when the solve moves forward (requires forecast data that is longer than the horizon at first)
......
...@@ -102,7 +102,11 @@ v_help_inc ...@@ -102,7 +102,11 @@ v_help_inc
// Unit Operation // Unit Operation
q_maxDownward q_maxDownward
q_maxDownwardOfflineReserve
q_maxUpward q_maxUpward
q_maxUpwardOfflineReserve
q_reserveProvision
q_reserveProvisionOnline
q_startshut q_startshut
q_startuptype q_startuptype
q_onlineLimit q_onlineLimit
...@@ -157,3 +161,4 @@ vq_gen ...@@ -157,3 +161,4 @@ vq_gen
vq_resDemand vq_resDemand
vq_resMissing vq_resMissing
v_stateSlack v_stateSlack
vq_capacity
...@@ -367,6 +367,7 @@ param_policy "Set of possible data parameters for grid, node, regulation" / ...@@ -367,6 +367,7 @@ param_policy "Set of possible data parameters for grid, node, regulation" /
reserveReliability "Reliability parameter of reserve provisions" reserveReliability "Reliability parameter of reserve provisions"
reserve_increase_ratio "Unit output is multiplied by this factor to get the increase in reserve demand" reserve_increase_ratio "Unit output is multiplied by this factor to get the increase in reserve demand"
portion_of_infeed_to_reserve "Proportion of the generation of a tripping unit that needs to be covered by reserves from other units" portion_of_infeed_to_reserve "Proportion of the generation of a tripping unit that needs to be covered by reserves from other units"
offlineReserveCapability "Proportion of an offline unit which can contribute to a category of reserve"
ReserveShareMax "Maximum reserve share of a group of units" ReserveShareMax "Maximum reserve share of a group of units"
LossOfTrans LossOfTrans
/ /
......
...@@ -83,7 +83,10 @@ Sets ...@@ -83,7 +83,10 @@ Sets
restypeDirectionGroup(restype, up_down, group) restypeDirectionGroup(restype, up_down, group)
restypeDirectionGridNodeGroup(restype, up_down, grid, node, group) restypeDirectionGridNodeGroup(restype, up_down, grid, node, group)
gnuRescapable(restype, up_down, grid, node, unit) "Units capable and available to provide particular reserves" gnuRescapable(restype, up_down, grid, node, unit) "Units capable and available to provide particular reserves"
gnuOfflineRescapable(restype, grid, node, unit) "Units capable and available to provide offline reserves"
restypeReleasedForRealization(restype) "Reserve types that are released for the realized time intervals" restypeReleasedForRealization(restype) "Reserve types that are released for the realized time intervals"
offlineRes (restype) "Reserve types where offline reserve provision possible"
offlineResUnit (unit) "Units where offline reserve provision possible"
* --- Sets to define time, forecasts and samples ------------------------------ * --- Sets to define time, forecasts and samples ------------------------------
$$include '%input_dir%/timeAndSamples.inc' $$include '%input_dir%/timeAndSamples.inc'
...@@ -131,7 +134,8 @@ Sets ...@@ -131,7 +134,8 @@ Sets
s_prev(s) "Temporary set for previous sample" s_prev(s) "Temporary set for previous sample"
$if defined scenario $if defined scenario
s_scenario(s, scenario) "Which samples belong to which scenarios" s_scenario(s, scenario) "Which samples belong to which scenarios"
gn_scenarios(*, node, *) "Which grid/flow, node and timeseries/param have data for long-term scenarios" gn_forecasts(*, node, timeseries) "Which grid/flow, node and timeseries use short-term forecasts"
gn_scenarios(*, node, timeseries) "Which grid/flow, node and timeseries have data for long-term scenarios"
* --- Sets used for the changing unit aggregation and efficiency approximations * --- Sets used for the changing unit aggregation and efficiency approximations
uft(unit, f, t) "Active units on intervals, enables aggregation of units for later intervals" uft(unit, f, t) "Active units on intervals, enables aggregation of units for later intervals"
......
...@@ -17,7 +17,6 @@ $offtext ...@@ -17,7 +17,6 @@ $offtext
* --- Internal counters ------------------------------------------------------- * --- Internal counters -------------------------------------------------------
Scalars Scalars
errorcount /0/
solveCount /0/ solveCount /0/
tSolveFirst "counter (ord) for the first t in the solve" tSolveFirst "counter (ord) for the first t in the solve"
tSolveLast "counter for the last t in the solve" tSolveLast "counter for the last t in the solve"
...@@ -95,7 +94,7 @@ Parameters ...@@ -95,7 +94,7 @@ Parameters
* --- Probability ------------------------------------------------------------- * --- Probability -------------------------------------------------------------
Parameters Parameters
p_msWeight(mType, s) "Weight of sample" p_msWeight(mType, s) "Temporal weight of sample: number of similar periods represented by sample s"
p_msProbability(mType, s) "Probability to reach sample conditioned on anchestor samples" p_msProbability(mType, s) "Probability to reach sample conditioned on anchestor samples"
p_mfProbability(mType, f) "Probability of forecast" p_mfProbability(mType, f) "Probability of forecast"
p_msft_probability(mType, s, f, t) "Probability of forecast" p_msft_probability(mType, s, f, t) "Probability of forecast"
...@@ -130,6 +129,7 @@ Parameters ...@@ -130,6 +129,7 @@ Parameters
df_reserves(grid, node, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves" df_reserves(grid, node, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves"
df_reservesGroup(group, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves" df_reservesGroup(group, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves"
df_scenario(f, t) "Forecast index displacement needed to get central forecast data for long-term scenarios" df_scenario(f, t) "Forecast index displacement needed to get central forecast data for long-term scenarios"
df_realization(f, t) "Displacement needed to reach the realized forecast on the current time step when no forecast is available"
// Sample displacement arrays // Sample displacement arrays
ds(s, t) "Displacement needed to reach the sample of previous time step" ds(s, t) "Displacement needed to reach the sample of previous time step"
...@@ -190,9 +190,6 @@ Parameters ...@@ -190,9 +190,6 @@ Parameters
p_tsMaxValue(*, node, timeseries) "Maximum allowed value of timeseries in grid/flow and node" p_tsMaxValue(*, node, timeseries) "Maximum allowed value of timeseries in grid/flow and node"
; ;
* Reset values for some parameters
Options clear = ts_influx_std, clear = ts_cf_std;
* --- Other time dependent parameters ----------------------------------------- * --- Other time dependent parameters -----------------------------------------
Parameters Parameters
p_storageValue(grid, node, t) "Value of stored something at the end of a time step" p_storageValue(grid, node, t) "Value of stored something at the end of a time step"
......
...@@ -414,6 +414,24 @@ gnuRescapable(restypeDirection(restype, up_down), gnu(grid, node, unit)) ...@@ -414,6 +414,24 @@ gnuRescapable(restypeDirection(restype, up_down), gnu(grid, node, unit))
} }
= yes; = yes;
// Units with offline reserve provision capabilities
gnuOfflineRescapable(restype, gnu(grid, node, unit))
$ { p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability')
}
= yes;
// Restypes with offline reserve provision possibility
offlineRes(restype)
$ {sum(gnu(grid, node, unit), p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability'))
}
= yes;
// Units with offline reserve provision possibility
offlineResUnit(unit)
$ {sum((gn(grid, node), restype), p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability'))
}
= yes;
// Node-node connections with reserve transfer capabilities // Node-node connections with reserve transfer capabilities
restypeDirectionGridNodeNode(restypeDirection(restype, up_down), gn2n(grid, node, node_)) restypeDirectionGridNodeNode(restypeDirection(restype, up_down), gn2n(grid, node, node_))
$ { p_gnnReserves(grid, node, node_, restype, up_down) $ { p_gnnReserves(grid, node, node_, restype, up_down)
...@@ -450,6 +468,7 @@ p_gnuReserves(gnu(grid, node, unit), restype, 'reserveReliability') ...@@ -450,6 +468,7 @@ p_gnuReserves(gnu(grid, node, unit), restype, 'reserveReliability')
= 1; = 1;
// Reserve provision overlap decreases the capacity of the overlapping category // Reserve provision overlap decreases the capacity of the overlapping category
loop(restype,
p_gnuReserves(gnu(grid, node, unit), restype, up_down) p_gnuReserves(gnu(grid, node, unit), restype, up_down)
${ gnuRescapable(restype, up_down, grid, node, unit) } ${ gnuRescapable(restype, up_down, grid, node, unit) }
= p_gnuReserves(grid, node, unit, restype, up_down) = p_gnuReserves(grid, node, unit, restype, up_down)
...@@ -457,6 +476,7 @@ p_gnuReserves(gnu(grid, node, unit), restype, up_down) ...@@ -457,6 +476,7 @@ p_gnuReserves(gnu(grid, node, unit), restype, up_down)
+ p_gnuReserves(grid, node, unit, restype_, up_down) + p_gnuReserves(grid, node, unit, restype_, up_down)
* p_gnuRes2Res(grid, node, unit, restype_, up_down, restype) * p_gnuRes2Res(grid, node, unit, restype_, up_down, restype)
); // END sum(restype_) ); // END sum(restype_)
);
* ============================================================================= * =============================================================================
* --- Data Integrity Checks --------------------------------------------------- * --- Data Integrity Checks ---------------------------------------------------
...@@ -539,7 +559,7 @@ loop( unit, ...@@ -539,7 +559,7 @@ loop( unit,
); // END loop(effLevelGroupUnit) ); // END loop(effLevelGroupUnit)
); );
* --- Check the start-up fuel fraction related data --------------------------- * --- Check fuel fraction related data ----------------------------------------
loop( unit_fuel(unit)${sum(fuel, uFuel(unit_fuel, 'startup', fuel))}, loop( unit_fuel(unit)${sum(fuel, uFuel(unit_fuel, 'startup', fuel))},
if(sum(fuel, p_uFuel(unit, 'startup', fuel, 'fixedFuelFraction')) <> 1, if(sum(fuel, p_uFuel(unit, 'startup', fuel, 'fixedFuelFraction')) <> 1,
...@@ -549,6 +569,14 @@ loop( unit_fuel(unit)${sum(fuel, uFuel(unit_fuel, 'startup', fuel))}, ...@@ -549,6 +569,14 @@ loop( unit_fuel(unit)${sum(fuel, uFuel(unit_fuel, 'startup', fuel))},
); );
); );
loop( unit_fuel(unit)${sum(fuel, p_uFuel(unit, 'main', fuel, 'maxFuelFraction'))},
if(sum(uFuel(unit, 'main', fuel), 1) < 2,
put log '!!! Error occurred on unit ' unit.tl:0 /;
put log '!!! Abort: maxFuelFraction cannot be applied to units with only a single main fuel!' /;
abort "'maxFuelFraction' cannot be applied to units with only a single main fuel!"
);
);
* --- Check the shutdown time related data ------------------------------------ * --- Check the shutdown time related data ------------------------------------
loop( unitStarttype(unit, starttypeConstrained), loop( unitStarttype(unit, starttypeConstrained),
...@@ -598,10 +626,14 @@ loop( (gnu(grid, node, unit), restypeDirection(restype, up_down)), ...@@ -598,10 +626,14 @@ loop( (gnu(grid, node, unit), restypeDirection(restype, up_down)),
* --- Default values --------------------------------------------------------- * --- Default values ---------------------------------------------------------
* ============================================================================= * =============================================================================
loop(timeseries$(not sameas(timeseries, 'ts_cf')), loop(timeseries$(not sameas(timeseries, 'ts_cf')),
p_autocorrelation(gn, timeseries) = 0;
p_tsMinValue(gn, timeseries) = -Inf; p_tsMinValue(gn, timeseries) = -Inf;
p_tsMaxValue(gn, timeseries) = Inf; p_tsMaxValue(gn, timeseries) = Inf;
); );
p_autocorrelation(flowNode, 'ts_cf') = 0;
p_tsMinValue(flowNode, 'ts_cf') = 0; p_tsMinValue(flowNode, 'ts_cf') = 0;
p_tsMaxValue(flowNode, 'ts_cf') = 1; p_tsMaxValue(flowNode, 'ts_cf') = 1;
* By default all nodes use forecasts for all timeseries
gn_forecasts(gn, timeseries) = yes;
gn_forecasts(flowNode, timeseries) = yes;
gn_forecasts(restype, node, 'ts_reserveDemand') = yes;
...@@ -61,9 +61,12 @@ equations ...@@ -61,9 +61,12 @@ equations
q_resDemandLargestInfeedTransferDown(restype, up_down, group, grid, node, node, s, f, t) "N-1 down reserve for transmission lines" q_resDemandLargestInfeedTransferDown(restype, up_down, group, grid, node, node, s, f, t) "N-1 down reserve for transmission lines"
q_resDemandLargestInfeedTransfer(restype, up_down, group, grid, node, node, s, f, t) "N-1 up/down reserve for transmission lines" q_resDemandLargestInfeedTransfer(restype, up_down, group, grid, node, node, s, f, t) "N-1 up/down reserve for transmission lines"
// Unit Operation // Unit Operation
q_maxDownward(grid, node, unit, mType, s, f, t) "Downward commitments will not undercut power plant minimum load constraints or maximum elec. consumption" q_maxDownward(grid, node, unit, mType, s, f, t) "Downward commitments (v_gen and online v_reserve) will not undercut minimum (online) production capacity (+) or maximum (online) consumption capacity (-)"
q_maxUpward(grid, node, unit, mType, s, f, t) "Upward commitments will not exceed maximum available capacity or consumed power" q_maxDownwardOfflineReserve(grid, node, unit, mType, s, f, t) "Downward commitments (v_gen and all v_reserve) will not undercut zero production (+) or maximum consumption capacity (-)"
q_reserveProvision(restype, up_down, grid, node, unit, s, f, t) "Reserve provision limited for units" q_maxUpward(grid, node, unit, mType, s, f, t) "Upward commitments (v_gen and online v_reserve) will not exceed maximum (online) production capacity (+) or minimum (online) consumption capacity (-)"
q_maxUpwardOfflineReserve(grid, node, unit, mType, s, f, t) "Upward commitments (v_gen and all v_reserve) will not exceed maximum production capacity (+) or zero consumption (-)"
q_reserveProvision(restype, up_down, grid, node, unit, s, f, t) "Reserve provision limited for units with investment possibility"
q_reserveProvisionOnline(restype, up_down, grid, node, unit, s, f, t) "Reserve provision limited for units that are not capable of providing offline reserve"
q_startshut(mType, s, unit, f, t) "Online capacity now minus online capacity in the previous interval is equal to started up minus shut down capacity" q_startshut(mType, s, unit, f, t) "Online capacity now minus online capacity in the previous interval is equal to started up minus shut down capacity"
q_startuptype(mType, s, starttype, unit, f, t) "Startup type depends on the time the unit has been non-operational" q_startuptype(mType, s, starttype, unit, f, t) "Startup type depends on the time the unit has been non-operational"
q_onlineOnStartUp(s, unit, f, t) "Unit must be online after starting up" q_onlineOnStartUp(s, unit, f, t) "Unit must be online after starting up"
......
...@@ -656,7 +656,9 @@ q_maxDownward(gnu(grid, node, unit), msft(m, s, f, t)) ...@@ -656,7 +656,9 @@ q_maxDownward(gnu(grid, node, unit), msft(m, s, f, t))
) // END sum(gngnu_constrainedOutputRatio) ) // END sum(gngnu_constrainedOutputRatio)
// Downward reserve participation // Downward reserve participation
- sum(gnuRescapable(restype, 'down', grid, node, unit)${ord(t) < tSolveFirst + p_gnReserves(grid, node, restype, 'reserve_length')}, - sum(gnuRescapable(restype, 'down', grid, node, unit)${ ord(t) < tSolveFirst + p_gnReserves(grid, node, restype, 'reserve_length')
and not gnuOfflineRescapable(restype, grid, node, unit)
},
+ v_reserve(restype, 'down', grid, node, unit, s, f+df_reserves(grid, node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve) + v_reserve(restype, 'down', grid, node, unit, s, f+df_reserves(grid, node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
) // END sum(nuRescapable) ) // END sum(nuRescapable)
...@@ -735,7 +737,65 @@ q_maxDownward(gnu(grid, node, unit), msft(m, s, f, t)) ...@@ -735,7 +737,65 @@ q_maxDownward(gnu(grid, node, unit), msft(m, s, f, t))
] // END * p_unit(availability) ] // END * p_unit(availability)
; ;
* --- Maximum Upwards Capacity ------------------------------------------------ * --- Maximum Downward Capacity for Production/Consumption, Online Reserves and Offline Reserves ---
q_maxDownwardOfflineReserve(gnu(grid, node, unit), msft(m, s, f, t))
${ gnuft(grid, node, unit, f, t)
and {
[ ord(t) < tSolveFirst + smax(restype, p_gnReserves(grid, node, restype, 'reserve_length')) // Unit is providing
and sum(restype, gnuRescapable(restype, 'down', grid, node, unit)) // downward reserves
]
}
and { sum(restype, gnuOfflineRescapable(restype, grid, node, unit))} // and it can provide some reserve products although being offline
}..
// Energy generation/consumption
+ v_gen(grid, node, unit, s, f, t)
// Considering output constraints (e.g. cV line)
+ sum(gngnu_constrainedOutputRatio(grid, node, grid_output, node_, unit),
+ p_gnu(grid_output, node_, unit, 'cV')
* v_gen(grid_output, node_, unit, s, f, t)
) // END sum(gngnu_constrainedOutputRatio)
// Downward reserve participation
- sum(gnuRescapable(restype, 'down', grid, node, unit)${ord(t) < tSolveFirst + p_gnReserves(grid, node, restype, 'reserve_length')},
+ v_reserve(restype, 'down', grid, node, unit, s, f+df_reserves(grid, node, restype, f, t), t)
) // END sum(nuRescapable)
=G= // Must be greater than maximum consumption
// Consuming units
// Available capacity restrictions
- p_unit(unit, 'availability') // Consumption units are also restricted by their (available) capacity
* [
// Capacity factors for flow units
+ sum(flowUnit(flow, unit),
+ ts_cf_(flow, node, f, t, s)
) // END sum(flow)
+ 1${not unit_flow(unit)}
] // END * p_unit(availability)
* [
// Existing capacity
+ p_gnu(grid, node, unit, 'maxCons')
// Investments to new capacity
+ [
+ p_gnu(grid, node, unit, 'unitSizeCons')
]
* [
+ sum(t_invest(t_)${ ord(t_)<=ord(t)
},
+ v_invest_LP(unit, t_)${unit_investLP(unit)}