Commit efa0f9ba authored by Juha Kiviluoma's avatar Juha Kiviluoma
Browse files

Merge branch 'dev' into dev_BB_in_Spine_VabiSys

parents 9ce0855d 330c64af
......@@ -4,9 +4,14 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
### Added
- Dynamic generation portfolios aka pathway modelling aka multi-year simulations with discounted costs enabled
- Reserve activation duration and reactivation time included (in state constraints)
### Changed
- Static inertia requirement can be fulfilled by both rotational inertia of machines and certain reserve products
- Parameters p_gnPolicy and p_groupPolicy3D replaced with p_groupPolicyEmission and p_groupPolicyUnit
### Fixed
- Updated the selection of unit efficiency approximation levels
## [1.2.1] - 2019-11-26
### Fixed
......
......@@ -190,6 +190,7 @@ Parameter
* settings(mSetting)
mSettings(mType, mSetting) "Model settings array"
mSettingsEff(mtype, effLevel) "Model efficiency approximation array"
mSettingsEff_start(mtype, effLevel) "The first time step of the efficiency level - mSettingsEff is the last"
mInterval(mType, mSetting, counter) "Model interval array"
t_skip_counter "Numerical counter for solve time steps"
;
......@@ -367,12 +368,14 @@ param_policy "Set of possible data parameters for groups or grid, node, regulati
staticInertia "A flag to indicate static inertia constraint should be implemented - q_inertiaMin"
dynamicInertia "A flag to indicate dynamic inertia constraint should be implemented - q_rateOfChangeOfFrequencyUnit/Transfer"
// Reserve related parameters, currently without a proper parameter set
update_frequency "Frequency of updating reserve contributions"
update_offset "Optional offset for delaying the reserve update frequency"
update_frequency "Frequency of updating reserve contributions (number of timesteps)"
update_offset "Optional offset for delaying the reserve update frequency (number of timesteps)"
gate_closure "Number of timesteps ahead of dispatch that reserves are fixed"
* use_time_series "Flag for using time series data. !!! REDUNDANT WITH useTimeseries, PENDING REMOVAL !!!"
useTimeSeries "Flag for using time series data"
reserve_length "Length of reserve horizon"
reserve_length "Length of reserve horizon (number of timesteps)"
reserve_activation_duration "How long the reserve should be provided once activated (h)"
reserve_reactivation_time "How soon the unit providing reserve needs to be able to reactivate after the start of the previous activation (h)"
reserveReliability "Reliability parameter of reserve provisions"
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"
......
......@@ -399,6 +399,23 @@ flowNode(flow, node)${ sum((f, t), ts_cf(flow, node, f, t))
// NOTE! Reserves can be disabled through the model settings file.
// The sets are disabled in "3a_periodicInit.gms" accordingly.
* --- Correct values for critical reserve related parameters - Part 1 ---------
// Reserve activation duration assumed to be 1 hour if not provided in data
p_groupReserves(group, restype, 'reserve_activation_duration')
${ not p_groupReserves(group, restype, 'reserve_activation_duration')
and p_groupReserves(group, restype, 'reserve_length')
}
= 1;
// Reserve reactivation time assumed to be 1 hour if not provided in data
p_groupReserves(group, restype, 'reserve_reactivation_time')
${ not p_groupReserves(group, restype, 'reserve_reactivation_time')
and p_groupReserves(group, restype, 'reserve_length')
}
= 1;
* --- Copy reserve data and create necessary sets -----------------------------
// Copy data from p_groupReserves to p_gnReserves
loop(gnGroup(grid, node, group)${sum(restype, p_groupReserves(group, restype, 'reserve_length'))},
p_gnReserves(grid, node, restype, param_policy) = p_groupReserves(group, restype, param_policy);
......@@ -454,7 +471,7 @@ restypeDirectionGridNodeGroup(restypeDirection(restype, up_down), gnGroup(grid,
}
= yes;
* --- Correct values for critical reserve related parameters ------------------
* --- Correct values for critical reserve related parameters - Part 2 ---------
// Reserve reliability assumed to be perfect if not provided in data
p_gnuReserves(gnu(grid, node, unit), restype, 'reserveReliability')
......@@ -586,14 +603,22 @@ loop( unitStarttype(unit, starttypeConstrained),
* --- Check reserve related data ----------------------------------------------
// Check that reserve_length is long enough for properly commitment of reserves
loop( restypeDirectionGridNode(restype, up_down, grid, node),
// Check that reserve_length is long enough for properly commitment of reserves
if(p_gnReserves(grid, node, restype, 'reserve_length') < p_gnReserves(grid, node, restype, 'update_frequency') + p_gnReserves(grid, node, restype, 'gate_closure'),
put log '!!! Error occurred on node ', node.tl:0 /;
loop( restypeDirectionGroup(restype, up_down, group),
// Check that reserve_length is long enough for proper commitment of reserves
if(p_groupReserves(group, restype, 'reserve_length') < p_groupReserves(group, restype, 'update_frequency') + p_groupReserves(group, restype, 'gate_closure'),
put log '!!! Error occurred on group ', group.tl:0 /;
put log '!!! Abort: The reserve_length parameter should be longer than update_frequency + gate_closure to fix the reserves properly!' /;
abort "The 'reserve_length' parameter should be longer than 'update_frequency' + 'gate_closure' to fix the reserves properly!"
); // END if
// Check that the duration of reserve activation is less than the reserve reactivation time
if(p_groupReserves(group, restype, 'reserve_reactivation_time') < p_groupReserves(group, restype, 'reserve_activation_duration'),
put log '!!! Error occurred on group ', group.tl:0 /;
put log '!!! Abort: The reserve_reactivation_time should be greater than or equal to the reserve_activation_duration!' /;
abort "The reserve_reactivation_time should be greater than or equal to the reserve_activation_duration!"
); // END if
); // END loop(restypeDirectionGroup)
loop( restypeDirectionGridNode(restype, up_down, grid, node),
// Check for each restype that a node does not belong to multiple groups
if(sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group), 1) > 1,
put log '!!! Error occurred on node ', node.tl:0 /;
......@@ -606,7 +631,7 @@ loop( restypeDirectionGridNode(restype, up_down, grid, node),
put log '!!! Abort: A node with reserve provision/transfer capability has to belong to a reserve node group!' /;
abort "A node with reserve provision/transfer capability has to belong to a reserve node group!"
); // END if
); // END loop(restypeDirectionNode)
); // END loop(restypeDirectionGridNode)
// Check that reserve overlaps are possible
loop( (gnu(grid, node, unit), restypeDirection(restype, up_down)),
......
......@@ -2308,6 +2308,8 @@ q_stateUpwardLimit(gn_state(grid, node), msft(m, s, f, t))
// Downward reserves from units that output energy to the node
+ sum(gnuRescapable(restype, 'down', grid_, node_input, unit)${ ord(t) < tSolveFirst + p_gnReserves(grid_, node_input, restype, 'reserve_length') },
+ v_reserve(restype, 'down', grid_, node_input, unit, s, f+df_reserves(grid_, node_input, restype, f, t), t)
* p_gnReserves(grid_, node_input, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_input, restype, 'reserve_reactivation_time')
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2320,6 +2322,8 @@ q_stateUpwardLimit(gn_state(grid, node), msft(m, s, f, t))
// Downward reserves from units that use the node as energy input
+ sum(gnuRescapable(restype, 'down', grid_, node_output, unit)${ ord(t) < tSolveFirst + p_gnReserves(grid_, node_output, restype, 'reserve_length') },
+ v_reserve(restype, 'down', grid_, node_output, unit, s, f+df_reserves(grid_, node_output, restype, f, t), t)
* p_gnReserves(grid_, node_output, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_output, restype, 'reserve_reactivation_time')
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2372,6 +2376,8 @@ q_stateDownwardLimit(gn_state(grid, node), msft(m, s, f, t))
// Upward reserves from units that output energy to the node
+ sum(gnuRescapable(restype, 'up', grid_, node_input, unit)${ ord(t) < tSolveFirst + p_gnReserves(grid_, node_input, restype, 'reserve_length') },
+ v_reserve(restype, 'up', grid_, node_input, unit, s, f+df_reserves(grid_, node_input, restype, f, t), t)
* p_gnReserves(grid_, node_input, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_input, restype, 'reserve_reactivation_time')
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2384,6 +2390,8 @@ q_stateDownwardLimit(gn_state(grid, node), msft(m, s, f, t))
// Upward reserves from units that use the node as energy input
+ sum(gnuRescapable(restype, 'up', grid_, node_output, unit)${ ord(t) < tSolveFirst + p_gnReserves(grid_, node_output, restype, 'reserve_length') },
+ v_reserve(restype, 'up', grid_, node_output, unit, s, f+df_reserves(grid_, node_output, restype, f, t), t)
* p_gnReserves(grid_, node_output, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_output, restype, 'reserve_reactivation_time')
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2414,6 +2422,8 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), msft(m, s, f, t)) ..
and ord(t) < tSolveFirst + p_gnReserves(grid_, node, restype, 'reserve_length')
},
+ v_reserve(restype, 'down', grid_, node_input, unit, s, f+df_reserves(grid_, node_input, restype, f, t), t)
* p_gnReserves(grid_, node_input, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_input, restype, 'reserve_reactivation_time')
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2427,6 +2437,8 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), msft(m, s, f, t)) ..
and ord(t) < tSolveFirst + p_gnReserves(grid_, node, restype, 'reserve_length')
},
+ v_reserve(restype, 'down', grid_, node_output, unit, s, f+df_reserves(grid_, node_output, restype, f, t), t)
* p_gnReserves(grid_, node_output, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_output, restype, 'reserve_reactivation_time')
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2459,6 +2471,8 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), msft(m, s, f, t)) ..
and ord(t) < tSolveFirst + p_gnReserves(grid_, node, restype, 'reserve_length')
},
+ v_reserve(restype, 'up', grid_, node_input, unit, s, f+df_reserves(grid_, node_input, restype, f, t), t)
* p_gnReserves(grid_, node_input, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_input, restype, 'reserve_reactivation_time')
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -2472,6 +2486,8 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), msft(m, s, f, t)) ..
and ord(t) < tSolveFirst + p_gnReserves(grid_, node, restype, 'reserve_length')
},
+ v_reserve(restype, 'up', grid_, node_output, unit, s, f+df_reserves(grid_, node_output, restype, f, t), t)
* p_gnReserves(grid_, node_output, restype, 'reserve_activation_duration')
/ p_gnReserves(grid_, node_output, restype, 'reserve_reactivation_time')
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......
......@@ -190,7 +190,7 @@ loop(m,
* --- Calculate 'lastStepNotAggregated' for aggregated units and aggregator units ---
loop(effLevel$mSettingsEff(m, effLevel),
loop(effLevel_${mSettingsEff(m, effLevel_) and ord(effLevel_) <= ord(effLevel)},
loop(effLevel_${mSettingsEff(m, effLevel_) and ord(effLevel_) < ord(effLevel)},
p_unit(unit_aggregated(unit), 'lastStepNotAggregated')${ sum(unit_,unitUnitEffLevel(unit_, unit, effLevel)) }
= mSettingsEff(m, effLevel_);
p_unit(unit_aggregator(unit), 'lastStepNotAggregated')${ sum(unit_,unitUnitEffLevel(unit, unit_, effLevel)) }
......@@ -237,6 +237,12 @@ loop(m,
);
);
);
// Store the first time step of the effLevel
loop(effLevel$mSettingsEff(m, effLevel),
loop(effLevel_${mSettingsEff(m, effLevel_) and ord(effLevel_) < ord(effLevel)},
mSettingsEff_start(m, effLevel) = mSettingsEff(m, effLevel_) + 1;
);
);
);
* --- Units with online variables in the first active effLevel ---------------
......
......@@ -612,7 +612,7 @@ Option clear = sufts;
// Loop over the defined efficiency groups for units
loop(effLevelGroupUnit(effLevel, effGroup, unit)${ mSettingsEff(mSolve, effLevel) },
// Determine the used effGroup for each uft
suft(effGroup, uft(unit, f, t))${ ord(t) >= tSolveFirst + mSettingsEff(mSolve, effLevel - 1) + 1
suft(effGroup, uft(unit, f, t))${ ord(t) >= tSolveFirst + mSettingsEff_start(mSolve, effLevel)
and ord(t) <= tSolveFirst + mSettingsEff(mSolve, effLevel) }
= yes;
); // END loop(effLevelGroupUnit)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment