Commit 4a2b7fce authored by Niina Helistö's avatar Niina Helistö
Browse files

First tests, disabling all reserve requirement constraints except q_resDemand....

First tests, disabling all reserve requirement constraints except q_resDemand. Adding some new parameters and sets (df_reservesGroup, restypeDirectionGroup, restypeDirectionGridNodeGroup) and changing the dimensions of some other parameters, sets and variables.
parent b89322d8
...@@ -23,11 +23,11 @@ Model schedule / ...@@ -23,11 +23,11 @@ Model schedule /
q_obj q_obj
q_balance q_balance
q_resDemand q_resDemand
q_resDemandLargestInfeedUnit * q_resDemandLargestInfeedUnit
q_rateOfChangeOfFrequencyUnit * q_rateOfChangeOfFrequencyUnit
q_rateOfChangeOfFrequencyTransfer * q_rateOfChangeOfFrequencyTransfer
q_resDemandLargestInfeedTransfer * q_resDemandLargestInfeedTransfer
q_resDemandLargestInfeedTransfer2 * q_resDemandLargestInfeedTransfer2
// Unit Operation // Unit Operation
q_maxDownward q_maxDownward
q_maxUpward q_maxUpward
......
...@@ -56,6 +56,7 @@ Sets ...@@ -56,6 +56,7 @@ Sets
flowNode(flow, node) "Nodes with flows" flowNode(flow, node) "Nodes with flows"
* --- Sets bounding geography and units --------------------------------------- * --- Sets bounding geography and units ---------------------------------------
group "A group of units, transfer links, nodes, etc."
gn(grid, node) "Grids and their nodes" gn(grid, node) "Grids and their nodes"
* NOTE! Should it be possible to permit time-series form upper or lower bounds on states? If so, then gn() needs rethinking. * NOTE! Should it be possible to permit time-series form upper or lower bounds on states? If so, then gn() needs rethinking.
gn2n(grid, node, node) "All (directional) transfer links between nodes in specific energy grids" gn2n(grid, node, node) "All (directional) transfer links between nodes in specific energy grids"
...@@ -79,6 +80,8 @@ Sets ...@@ -79,6 +80,8 @@ Sets
restypeDirection(restype, up_down) "Different combinations of reserve types and directions" restypeDirection(restype, up_down) "Different combinations of reserve types and directions"
restypeDirectionNode(restype, up_down, node) "Nodes with reserve requirements" restypeDirectionNode(restype, up_down, node) "Nodes with reserve requirements"
resTypeDirectionNodeNode(restype, up_down, node, node) "Node node connections that can transfer reserves" resTypeDirectionNodeNode(restype, up_down, node, node) "Node node connections that can transfer reserves"
restypeDirectionGroup(restype, up_down, group)
restypeDirectionGridNodeGroup(restype, up_down, grid, node, group)
nuRescapable(restype, up_down, node, unit) "Units capable and available to provide particular reserves" nuRescapable(restype, up_down, node, unit) "Units capable and available to provide particular 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"
...@@ -102,7 +105,7 @@ Sets ...@@ -102,7 +105,7 @@ Sets
ft(f, t) "Combination of forecasts and t:s in the current solve" ft(f, t) "Combination of forecasts and t:s in the current solve"
ft_realized(f, t) "Realized ft" ft_realized(f, t) "Realized ft"
ft_realizedNoReset(f, t) "Full set of realized ft, facilitates calculation of results" ft_realizedNoReset(f, t) "Full set of realized ft, facilitates calculation of results"
ft_reservesFixed(node, restype, f, t) "Forecast-time steps with reserves fixed due to commitments on a previous solve." ft_reservesFixed(group, restype, f, t) "Forecast-time steps with reserves fixed due to commitments on a previous solve."
mft(mType, f, t) "Combination of forecasts and t:s in the current model solve" mft(mType, f, t) "Combination of forecasts and t:s in the current model solve"
msf(mType, s, f) "Combination of samples and forecasts in the models" msf(mType, s, f) "Combination of samples and forecasts in the models"
msft(mType, s, f, t) "Combination of models, samples, forecasts and t's" msft(mType, s, f, t) "Combination of models, samples, forecasts and t's"
...@@ -156,7 +159,6 @@ $if defined scenario ...@@ -156,7 +159,6 @@ $if defined scenario
shutdownCounter(unit, counter) "Counter used for unit shutdown intervals" shutdownCounter(unit, counter) "Counter used for unit shutdown intervals"
* --- Sets used for grouping of units, transfer links, nodes, etc. ------------ * --- Sets used for grouping of units, transfer links, nodes, etc. ------------
group "A group of units, transfer links, nodes, etc."
uGroup(unit, group) "Units in particular groups" uGroup(unit, group) "Units in particular groups"
gnuGroup(grid, node, unit, group) "Combination of grids, nodes and units in particular groups" gnuGroup(grid, node, unit, group) "Combination of grids, nodes and units in particular groups"
gn2nGroup(grid, node, node, group) "Transfer links in particular groups" gn2nGroup(grid, node, node, group) "Transfer links in particular groups"
......
...@@ -52,6 +52,7 @@ Parameters ...@@ -52,6 +52,7 @@ Parameters
p_gnuBoundaryProperties(grid, node, unit, slack, param_gnuBoundaryProperties) "Properties for unit boundaries where energy type matters" p_gnuBoundaryProperties(grid, node, unit, slack, param_gnuBoundaryProperties) "Properties for unit boundaries where energy type matters"
p_unit(unit, param_unit) "Unit data where energy type does not matter" p_unit(unit, param_unit) "Unit data where energy type does not matter"
p_nReserves(node, restype, *) "Data defining the reserve rules in each node" p_nReserves(node, restype, *) "Data defining the reserve rules in each node"
p_groupReserves(group, restype, *) "Data defining the reserve rules in each node group"
p_nReserves3D(node, restype, up_down, param_policy) "Reserve policy in each node" p_nReserves3D(node, restype, up_down, param_policy) "Reserve policy in each node"
p_nuReserves(node, unit, restype, *) "Reserve provision data for units" p_nuReserves(node, unit, restype, *) "Reserve provision data for units"
p_nnReserves(node, node, restype, up_down) "Reserve provision data for node node connections" p_nnReserves(node, node, restype, up_down) "Reserve provision data for node node connections"
...@@ -126,6 +127,7 @@ Parameters ...@@ -126,6 +127,7 @@ Parameters
df(f, t) "Displacement needed to reach the realized forecast on the current time step" df(f, t) "Displacement needed to reach the realized forecast on the current time step"
df_central(f, t) "Displacement needed to reach the central forecast - this is needed when the forecast tree gets reduced in dynamic equations" df_central(f, t) "Displacement needed to reach the central forecast - this is needed when the forecast tree gets reduced in dynamic equations"
df_reserves(node, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves" df_reserves(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_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"
// Sample displacement arrays // Sample displacement arrays
...@@ -151,7 +153,7 @@ Parameters ...@@ -151,7 +153,7 @@ Parameters
// Used mostly for raw data storage // Used mostly for raw data storage
ts_influx(grid, node, f, t) "External power inflow/outflow during a time step (MWh/h)" ts_influx(grid, node, f, t) "External power inflow/outflow during a time step (MWh/h)"
ts_cf(flow, node, f, t) "Available capacity factor time series (p.u.)" ts_cf(flow, node, f, t) "Available capacity factor time series (p.u.)"
ts_reserveDemand(restype, up_down, node, f, t) "Reserve demand in region in the time step (MW)" ts_reserveDemand(restype, up_down, group, f, t) "Reserve demand in region in the time step (MW)"
ts_node(grid, node, param_gnBoundaryTypes, f, t) "Fix the states of a node according to time-series form exogenous input ([v_state])" ts_node(grid, node, param_gnBoundaryTypes, f, t) "Fix the states of a node according to time-series form exogenous input ([v_state])"
ts_fuelPriceChange(fuel, t) "Initial fuel price and consequent changes in fuel price (EUR/MWh)" ts_fuelPriceChange(fuel, t) "Initial fuel price and consequent changes in fuel price (EUR/MWh)"
ts_fuelPrice(fuel, t) "Fuel price time series (EUR/MWh)" ts_fuelPrice(fuel, t) "Fuel price time series (EUR/MWh)"
...@@ -161,7 +163,7 @@ Parameters ...@@ -161,7 +163,7 @@ Parameters
// NOTE: Sample dimension has to be last because of the scenario reduction algorithm // NOTE: Sample dimension has to be last because of the scenario reduction algorithm
ts_influx_(grid, node, f, t, s) "Mean external power inflow/outflow during a time step (MWh/h)" ts_influx_(grid, node, f, t, s) "Mean external power inflow/outflow during a time step (MWh/h)"
ts_cf_(flow, node, f, t, s) "Mean available capacity factor time series (p.u.)" ts_cf_(flow, node, f, t, s) "Mean available capacity factor time series (p.u.)"
ts_reserveDemand_(restype, up_down, node, f, t) "Mean reserve demand in region in the time step (MW)" ts_reserveDemand_(restype, up_down, group, f, t) "Mean reserve demand in region in the time step (MW)"
ts_node_(grid, node, param_gnBoundaryTypes, f, t, s) "Mean value of ts_node" ts_node_(grid, node, param_gnBoundaryTypes, f, t, s) "Mean value of ts_node"
ts_fuelPrice_(fuel, t) "Mean fuel price time during time step (EUR/MWh)" ts_fuelPrice_(fuel, t) "Mean fuel price time during time step (EUR/MWh)"
...@@ -171,7 +173,7 @@ Parameters ...@@ -171,7 +173,7 @@ Parameters
ts_effGroupUnit_update(effSelector, unit, param_eff, f, t) ts_effGroupUnit_update(effSelector, unit, param_eff, f, t)
ts_influx_update(grid, node, f, t) ts_influx_update(grid, node, f, t)
ts_cf_update(flow, node, f, t) ts_cf_update(flow, node, f, t)
ts_reserveDemand_update(restype, up_down, node, f, t) ts_reserveDemand_update(restype, up_down, group, f, t)
ts_node_update(grid, node, param_gnBoundaryTypes, f, t) ts_node_update(grid, node, param_gnBoundaryTypes, f, t)
ts_fuelPriceChange_update(fuel, t) ts_fuelPriceChange_update(fuel, t)
ts_unavailability_update(unit, t) ts_unavailability_update(unit, t)
......
...@@ -136,7 +136,7 @@ Parameters ...@@ -136,7 +136,7 @@ Parameters
r_reserve2Reserve(restype, up_down, node, unit, restype, f, t) "Reserve provided for another reserve category (MW) (also included in r_reserve - this is just for debugging)" r_reserve2Reserve(restype, up_down, node, unit, restype, f, t) "Reserve provided for another reserve category (MW) (also included in r_reserve - this is just for debugging)"
// Interesting reserve results // Interesting reserve results
r_resDemandMarginal(restype, up_down, node, f, t) "Marginal values of the q_resDemand equation" r_resDemandMarginal(restype, up_down, group, f, t) "Marginal values of the q_resDemand equation"
r_nuTotalReserve(restype, up_down, node, unit) "Total nu reserve provision over the simulation (MW*h)" r_nuTotalReserve(restype, up_down, node, unit) "Total nu reserve provision over the simulation (MW*h)"
r_nuTotalReserveShare(restype, up_down, node, unit) "Total nu/n reserve provision share over the simulation" r_nuTotalReserveShare(restype, up_down, node, unit) "Total nu/n reserve provision share over the simulation"
r_nTotalReserve(restype, up_down, node) "Total reserve provisions in nodes over the simulation (MW*h)" r_nTotalReserve(restype, up_down, node) "Total reserve provisions in nodes over the simulation (MW*h)"
...@@ -154,9 +154,9 @@ Parameters ...@@ -154,9 +154,9 @@ Parameters
r_qGen(inc_dec, grid, node, f, t) "Dummy energy generation (increase) or consumption (generation decrease) to ensure equation feasibility (MW)" r_qGen(inc_dec, grid, node, f, t) "Dummy energy generation (increase) or consumption (generation decrease) to ensure equation feasibility (MW)"
r_gnTotalqGen(inc_dec, grid, node) "Total dummy energy generation/consumption in gn over the simulation (MWh)." r_gnTotalqGen(inc_dec, grid, node) "Total dummy energy generation/consumption in gn over the simulation (MWh)."
r_gTotalqGen(inc_dec, grid) "Total dummy energy generation/consumption in g over the simulation (MWh)." r_gTotalqGen(inc_dec, grid) "Total dummy energy generation/consumption in g over the simulation (MWh)."
r_qResDemand(restype, up_down, node, f, t) "Dummy to decrease demand for a reserve (MW) before reserve commitment" r_qResDemand(restype, up_down, group, f, t) "Dummy to decrease demand for a reserve (MW) before reserve commitment"
r_qResMissing(restype, up_down, node, f, t) "Dummy to decrease demand for a reserve (MW) after reserve commitment" r_qResMissing(restype, up_down, group, f, t) "Dummy to decrease demand for a reserve (MW) after reserve commitment"
r_nTotalqResDemand(restype, up_down, node) "Total dummy reserve provisions in n over the simulation" r_nTotalqResDemand(restype, up_down, group) "Total dummy reserve provisions in n over the simulation"
r_qCapacity(grid, node, f, t) "Dummy capacity to ensure capacity margin equation feasibility (MW)" r_qCapacity(grid, node, f, t) "Dummy capacity to ensure capacity margin equation feasibility (MW)"
r_solveStatus(t, solve_info) "Information about the solve" r_solveStatus(t, solve_info) "Information about the solve"
......
...@@ -32,6 +32,7 @@ $ifthen exist '%input_dir%/inputData.gdx' ...@@ -32,6 +32,7 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc unitUnitEffLevel $$loaddc unitUnitEffLevel
$$loaddc uFuel $$loaddc uFuel
$$loaddc effLevelGroupUnit $$loaddc effLevelGroupUnit
$$loaddc group
$$loaddc p_gn $$loaddc p_gn
$$loaddc p_gnn $$loaddc p_gnn
$$loaddc p_gnu $$loaddc p_gnu
...@@ -41,7 +42,7 @@ $ifthen exist '%input_dir%/inputData.gdx' ...@@ -41,7 +42,7 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc restype $$loaddc restype
$$loaddc restypeDirection $$loaddc restypeDirection
$$loaddc restypeReleasedForRealization $$loaddc restypeReleasedForRealization
$$loaddc p_nReserves $$loaddc p_groupReserves
$$loaddc p_nReserves3D $$loaddc p_nReserves3D
$$loaddc p_nuReserves $$loaddc p_nuReserves
$$loaddc p_nnReserves $$loaddc p_nnReserves
...@@ -62,7 +63,6 @@ $ifthen exist '%input_dir%/inputData.gdx' ...@@ -62,7 +63,6 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc ts_node $$loaddc ts_node
$$loaddc t_invest $$loaddc t_invest
$$loaddc p_storageValue $$loaddc p_storageValue
$$loaddc group
$$loaddc uGroup $$loaddc uGroup
$$loaddc gnuGroup $$loaddc gnuGroup
$$loaddc gn2nGroup $$loaddc gn2nGroup
...@@ -401,6 +401,12 @@ flowNode(flow, node)${ sum((f, t), ts_cf(flow, node, f, t)) ...@@ -401,6 +401,12 @@ flowNode(flow, node)${ sum((f, t), ts_cf(flow, node, f, t))
// NOTE! Reserves can be disabled through the model settings file. // NOTE! Reserves can be disabled through the model settings file.
// The sets are disabled in "3a_periodicInit.gms" accordingly. // The sets are disabled in "3a_periodicInit.gms" accordingly.
// Copy data from p_groupReserves to p_nReserves
loop(gnGroup(grid, node, group)${sum(restype, p_groupReserves(group, restype, 'reserve_length'))},
p_nReserves(node, restype, param_policy) = p_groupReserves(group, restype, param_policy);
p_nReserves(node, restype, up_down) = p_groupReserves(group, restype, up_down);
);
// Units with reserve provision capabilities // Units with reserve provision capabilities
nuRescapable(restypeDirection(restype, up_down), nu(node, unit)) nuRescapable(restypeDirection(restype, up_down), nu(node, unit))
$ { p_nuReserves(node, unit, restype, up_down) $ { p_nuReserves(node, unit, restype, up_down)
...@@ -423,6 +429,16 @@ restypeDirectionNode(restypeDirection(restype, up_down), node) ...@@ -423,6 +429,16 @@ restypeDirectionNode(restypeDirection(restype, up_down), node)
} }
= yes; = yes;
// Groups with reserve requirements
restypeDirectionGroup(restypeDirection(restype, up_down), group)
$ { p_groupReserves(group, restype, 'reserve_length')
}
= yes;
restypeDirectionGridNodeGroup(restypeDirection(restype, up_down), gnGroup(grid, node, group))
$ { p_groupReserves(group, restype, 'reserve_length')
}
= yes;
* --- Correct values for critical reserve related parameters ------------------ * --- Correct values for critical reserve related parameters ------------------
// Reserve reliability assumed to be perfect if not provided in data // Reserve reliability assumed to be perfect if not provided in data
...@@ -547,11 +563,24 @@ loop( unitStarttype(unit, starttypeConstrained), ...@@ -547,11 +563,24 @@ loop( unitStarttype(unit, starttypeConstrained),
// Check that reserve_length is long enough for properly commitment of reserves // Check that reserve_length is long enough for properly commitment of reserves
loop( restypeDirectionNode(restype, up_down, node), loop( restypeDirectionNode(restype, up_down, node),
// Check that reserve_length is long enough for properly commitment of reserves
if(p_nReserves(node, restype, 'reserve_length') < p_nReserves(node, restype, 'update_frequency') + p_nReserves(node, restype, 'gate_closure'), if(p_nReserves(node, restype, 'reserve_length') < p_nReserves(node, restype, 'update_frequency') + p_nReserves(node, restype, 'gate_closure'),
put log '!!! Error occurred on node ', node.tl:0 /; put log '!!! Error occurred on node ', node.tl:0 /;
put log '!!! Abort: The reserve_length parameter should be longer than update_frequency + gate_closure to fix the reserves properly!' /; 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!" abort "The 'reserve_length' parameter should be longer than 'update_frequency' + 'gate_closure' to fix the reserves properly!"
); // END if ); // END if
// 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 /;
put log '!!! Abort: For each reserve type, a node can belong to at maximum one reserve node group!' /;
abort "For each reserve type, a node can belong to at maximum one reserve node group!"
); // END if
// Check if there are units/interconnections connected to a node that does not belong to any restypeDirectionGroup
if(sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group), 1) < 1,
put log '!!! Error occurred on node ', node.tl:0 /;
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(restypeDirectionNode)
// Check that reserve overlaps are possible // Check that reserve overlaps are possible
......
...@@ -56,8 +56,8 @@ Positive variables ...@@ -56,8 +56,8 @@ Positive variables
Positive variables Positive variables
v_stateSlack(grid, node, slack, s, f, t) "Slack variable for different v_state slack categories, permits e.g. costs for exceeding acceptable v_states (MWh, unless modified by energyCapacity parameter)" v_stateSlack(grid, node, slack, s, f, t) "Slack variable for different v_state slack categories, permits e.g. costs for exceeding acceptable v_states (MWh, unless modified by energyCapacity parameter)"
vq_gen(inc_dec, grid, node, s, f, t) "Dummy energy generation (increase) or consumption (generation decrease) to ensure equation feasibility (MW)" vq_gen(inc_dec, grid, node, s, f, t) "Dummy energy generation (increase) or consumption (generation decrease) to ensure equation feasibility (MW)"
vq_resDemand(restype, up_down, node, s, f, t) "Dummy to decrease demand for a reserve (MW) before the reserve has been locked" vq_resDemand(restype, up_down, group, s, f, t) "Dummy to decrease demand for a reserve (MW) before the reserve has been locked"
vq_resMissing(restype, up_down, node, s, f, t) "Dummy to decrease demand for a reserve (MW) after the reserve has been locked" vq_resMissing(restype, up_down, group, s, f, t) "Dummy to decrease demand for a reserve (MW) after the reserve has been locked"
vq_capacity(grid, node, s, f, t) "Dummy variable to ensure capacity margin equation feasibility (MW)" vq_capacity(grid, node, s, f, t) "Dummy variable to ensure capacity margin equation feasibility (MW)"
; ;
...@@ -53,7 +53,7 @@ equations ...@@ -53,7 +53,7 @@ equations
// Objective Function, Energy Balance, and Reserve demand // Objective Function, Energy Balance, and Reserve demand
q_obj "Objective function" q_obj "Objective function"
q_balance(grid, node, mType, s, f, t) "Energy demand must be satisfied at each node" q_balance(grid, node, mType, s, f, t) "Energy demand must be satisfied at each node"
q_resDemand(restype, up_down, node, s, f, t) "Procurement for each reserve type is greater than demand" q_resDemand(restype, up_down, group, s, f, t) "Procurement for each reserve type is greater than demand"
q_resDemandLargestInfeedUnit(grid, restype, up_down, node, unit, s, f, t) "N-1 Reserve" q_resDemandLargestInfeedUnit(grid, restype, up_down, node, unit, s, f, t) "N-1 Reserve"
q_rateOfChangeOfFrequencyUnit(group, unit, s, f, t) "N-1 unit contingency with ROCOF" q_rateOfChangeOfFrequencyUnit(group, unit, s, f, t) "N-1 unit contingency with ROCOF"
q_rateOfChangeOfFrequencyTransfer(group, grid, node, node, s, f, t) "N-1 transmission line contingency with ROCOF" q_rateOfChangeOfFrequencyTransfer(group, grid, node, node, s, f, t) "N-1 transmission line contingency with ROCOF"
......
...@@ -72,10 +72,10 @@ q_obj .. ...@@ -72,10 +72,10 @@ q_obj ..
) // END sum(inc_dec) ) // END sum(inc_dec)
// Reserve provision feasibility dummy variable penalties // Reserve provision feasibility dummy variable penalties
+ sum(restypeDirectionNode(restype, up_down, node), + sum(restypeDirectionGroup(restype, up_down, group),
+ vq_resDemand(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t) + vq_resDemand(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t)
* PENALTY_RES(restype, up_down) * PENALTY_RES(restype, up_down)
+ vq_resMissing(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t)${ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) } + vq_resMissing(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t)${ ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t) }
* PENALTY_RES_MISSING(restype, up_down) * PENALTY_RES_MISSING(restype, up_down)
) // END sum(restypeDirectionNode) ) // END sum(restypeDirectionNode)
......
...@@ -90,23 +90,23 @@ q_balance(gn(grid, node), msft(m, s, f, t)) // Energy/power balance dynamics sol ...@@ -90,23 +90,23 @@ q_balance(gn(grid, node), msft(m, s, f, t)) // Energy/power balance dynamics sol
// NOTE! Currently, there are multiple identical instances of the reserve balance equation being generated for each forecast branch even when the reserves are committed and identical between the forecasts. // NOTE! Currently, there are multiple identical instances of the reserve balance equation being generated for each forecast branch even when the reserves are committed and identical between the forecasts.
// NOTE! This could be solved by formulating a new "ft_reserves" set to cover only the relevant forecast-time steps, but it would possibly make the reserves even more confusing. // NOTE! This could be solved by formulating a new "ft_reserves" set to cover only the relevant forecast-time steps, but it would possibly make the reserves even more confusing.
q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t)) q_resDemand(restypeDirectionGroup(restype, up_down, group), sft(s, f, t))
${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length') ${ ord(t) < tSolveFirst + p_groupReserves(group, restype, 'reserve_length')
and not [ restypeReleasedForRealization(restype) and not [ restypeReleasedForRealization(restype)
and sft_realized(s, f, t)] and sft_realized(s, f, t)]
} .. } ..
// Reserve provision by capable units on this node // Reserve provision by capable units on this group
+ sum(nuft(node, unit, f, t)${nuRescapable(restype, up_down, node, unit)}, + sum(gnuft(grid, node, unit, f, t)${gnGroup(grid, node, group) and nuRescapable(restype, up_down, node, unit)},
+ v_reserve(restype, up_down, node, unit, s, f+df_reserves(node, restype, f, t), t) + v_reserve(restype, up_down, node, unit, s, f+df_reserves(node, restype, f, t), t)
* [ // Account for reliability of reserves * [ // Account for reliability of reserves
+ 1${sft_realized(s, f+df_reserves(node, restype, f, t), t)} // reserveReliability limits the reliability of reserves locked ahead of time. + 1${sft_realized(s, f+df_reserves(node, restype, f, t), t)} // reserveReliability limits the reliability of reserves locked ahead of time.
+ p_nuReserves(node, unit, restype, 'reserveReliability')${not sft_realized(s, f+df_reserves(node, restype, f, t), t)} + p_nuReserves(node, unit, restype, 'reserveReliability')${not sft_realized(s, f+df_reserves(node, restype, f, t), t)}
] // END * v_reserve ] // END * v_reserve
) // END sum(nuft) ) // END sum(gnuft)
// Reserve provision from other reserve categories when they can be shared // Reserve provision from other reserve categories when they can be shared
+ sum((nuft(node, unit, f, t), restype_)${p_nuRes2Res(node, unit, restype_, up_down, restype)}, + sum((gnuft(grid, node, unit, f, t), restype_)${gnGroup(grid, node, group) and p_nuRes2Res(node, unit, restype_, up_down, restype)},
+ v_reserve(restype_, up_down, node, unit, s, f+df_reserves(node, restype_, f, t), t) + v_reserve(restype_, up_down, node, unit, s, f+df_reserves(node, restype_, f, t), t)
* p_nuRes2Res(node, unit, restype_, up_down, restype) * p_nuRes2Res(node, unit, restype_, up_down, restype)
* [ // Account for reliability of reserves * [ // Account for reliability of reserves
...@@ -114,14 +114,20 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t)) ...@@ -114,14 +114,20 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t))
+ p_nuReserves(node, unit, restype, 'reserveReliability')${not sft_realized(s, f+df_reserves(node, restype, f, t), t)} + p_nuReserves(node, unit, restype, 'reserveReliability')${not sft_realized(s, f+df_reserves(node, restype, f, t), t)}
* p_nuReserves(node, unit, restype_, 'reserveReliability') * p_nuReserves(node, unit, restype_, 'reserveReliability')
] // END * v_reserve ] // END * v_reserve
) // END sum(nuft) ) // END sum(gnuft)
// Reserve provision to this node via transfer links // Reserve provision to this group via transfer links
+ sum(gn2n_directional(grid, node_, node)${restypeDirectionNodeNode(restype, up_down, node_, node)}, + sum(gn2n_directional(grid, node_, node)${gnGroup(grid, node, group)
and not gnGroup(grid, node_, group)
and restypeDirectionNodeNode(restype, up_down, node_, node)
},
+ (1 - p_gnn(grid, node_, node, 'transferLoss') ) + (1 - p_gnn(grid, node_, node, 'transferLoss') )
* v_resTransferRightward(restype, up_down, node_, node, s, f+df_reserves(node_, restype, f, t), t) // Reserves from another node - reduces the need for reserves in the node * v_resTransferRightward(restype, up_down, node_, node, s, f+df_reserves(node_, restype, f, t), t) // Reserves from another node - reduces the need for reserves in the node
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
+ sum(gn2n_directional(grid, node, node_)${restypeDirectionNodeNode(restype, up_down, node_, node)}, + sum(gn2n_directional(grid, node, node_)${gnGroup(grid, node, group)
and not gnGroup(grid, node_, group)
and restypeDirectionNodeNode(restype, up_down, node_, node)
},
+ (1 - p_gnn(grid, node, node_, 'transferLoss') ) + (1 - p_gnn(grid, node, node_, 'transferLoss') )
* v_resTransferLeftward(restype, up_down, node, node_, s, f+df_reserves(node_, restype, f, t), t) // Reserves from another node - reduces the need for reserves in the node * v_resTransferLeftward(restype, up_down, node, node_, s, f+df_reserves(node_, restype, f, t), t) // Reserves from another node - reduces the need for reserves in the node
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
...@@ -129,26 +135,32 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t)) ...@@ -129,26 +135,32 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t))
=G= =G=
// Demand for reserves // Demand for reserves
+ ts_reserveDemand(restype, up_down, node, f, t)${p_nReserves(node, restype, 'use_time_series')} + ts_reserveDemand(restype, up_down, group, f, t)${p_groupReserves(group, restype, 'use_time_series')}
+ p_nReserves(node, restype, up_down)${not p_nReserves(node, restype, 'use_time_series')} + p_groupReserves(group, restype, up_down)${not p_groupReserves(group, restype, 'use_time_series')}
// Reserve demand increase because of units // Reserve demand increase because of units
+ sum(nuft(node, unit, f, t)${p_nuReserves(node, unit, restype, 'reserve_increase_ratio')}, // Could be better to have 'reserve_increase_ratio' separately for up and down directions + sum(gnuft(grid, node, unit, f, t)${gnGroup(grid, node, group) and p_nuReserves(node, unit, restype, 'reserve_increase_ratio')}, // Could be better to have 'reserve_increase_ratio' separately for up and down directions
+ sum(gnu(grid, node, unit), v_gen(grid, node, unit, s, f, t)) // Reserve sets and variables are currently lacking the grid dimension... + sum(gnu(grid, node, unit), v_gen(grid, node, unit, s, f, t)) // Reserve sets and variables are currently lacking the grid dimension...
* p_nuReserves(node, unit, restype, 'reserve_increase_ratio') * p_nuReserves(node, unit, restype, 'reserve_increase_ratio')
) // END sum(nuft) ) // END sum(nuft)
// Reserve provisions to another nodes via transfer links // Reserve provisions to other groups via transfer links
+ sum(gn2n_directional(grid, node, node_)${restypeDirectionNodeNode(restype, up_down, node, node_)}, // If trasferring reserves to another node, increase your own reserves by same amount + sum(gn2n_directional(grid, node, node_)${gnGroup(grid, node, group)
and not gnGroup(grid, node_, group)
and restypeDirectionNodeNode(restype, up_down, node, node_)
}, // If trasferring reserves to another node, increase your own reserves by same amount
+ v_resTransferRightward(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t) + v_resTransferRightward(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t)
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
+ sum(gn2n_directional(grid, node_, node)${restypeDirectionNodeNode(restype, up_down, node, node_)}, // If trasferring reserves to another node, increase your own reserves by same amount + sum(gn2n_directional(grid, node_, node)${gnGroup(grid, node, group)
and not gnGroup(grid, node_, group)
and restypeDirectionNodeNode(restype, up_down, node, node_)
}, // If trasferring reserves to another node, increase your own reserves by same amount
+ v_resTransferLeftward(restype, up_down, node_, node, s, f+df_reserves(node, restype, f, t), t) + v_resTransferLeftward(restype, up_down, node_, node, s, f+df_reserves(node, restype, f, t), t)
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables // Reserve demand feasibility dummy variables
- vq_resDemand(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t) - vq_resDemand(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t)
- vq_resMissing(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} - vq_resMissing(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t)${ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t)}
; ;
* --- N-1 Reserve Demand ---------------------------------------------------------- * --- N-1 Reserve Demand ----------------------------------------------------------
...@@ -208,8 +220,8 @@ q_resDemandLargestInfeedUnit(grid, restypeDirectionNode(restype, 'up', node), un ...@@ -208,8 +220,8 @@ q_resDemandLargestInfeedUnit(grid, restypeDirectionNode(restype, 'up', node), un
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables // Reserve demand feasibility dummy variables
- vq_resDemand(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t) * - vq_resDemand(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)
- vq_resMissing(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} * - vq_resMissing(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
; ;
q_rateOfChangeOfFrequencyUnit(group, unit_fail(unit_), sft(s, f, t)) q_rateOfChangeOfFrequencyUnit(group, unit_fail(unit_), sft(s, f, t))
...@@ -366,8 +378,8 @@ q_resDemandLargestInfeedTransfer(grid, restypeDirectionNode(restype, 'up', node) ...@@ -366,8 +378,8 @@ q_resDemandLargestInfeedTransfer(grid, restypeDirectionNode(restype, 'up', node)
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables // Reserve demand feasibility dummy variables
- vq_resDemand(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t) * - vq_resDemand(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)
- vq_resMissing(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} * - vq_resMissing(restype, 'up', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
; ;
* --- N-1 Downward reserve demand due to a possibility that an interconnector that is transferring power to the node fails ------------------------------------------------- * --- N-1 Downward reserve demand due to a possibility that an interconnector that is transferring power to the node fails -------------------------------------------------
...@@ -438,8 +450,8 @@ q_resDemandLargestInfeedTransfer2(grid, restypeDirectionNode(restype, 'down', no ...@@ -438,8 +450,8 @@ q_resDemandLargestInfeedTransfer2(grid, restypeDirectionNode(restype, 'down', no
) // END sum(gn2n_directional) ) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables // Reserve demand feasibility dummy variables
- vq_resDemand(restype, 'down', node, s, f+df_reserves(node, restype, f, t), t) * - vq_resDemand(restype, 'down', node, s, f+df_reserves(node, restype, f, t), t)
- vq_resMissing(restype, 'down', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} * - vq_resMissing(restype, 'down', node, s, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
; ;
* --- N-1 Upward reserve demand due to a possibility that an interconnector that is transferring power to the node fails ------------------------------------------------- * --- N-1 Upward reserve demand due to a possibility that an interconnector that is transferring power to the node fails -------------------------------------------------
...@@ -738,7 +750,8 @@ q_reserveProvision(nuRescapable(restypeDirectionNode(restype, up_down, node), un ...@@ -738,7 +750,8 @@ q_reserveProvision(nuRescapable(restypeDirectionNode(restype, up_down, node), un
${ ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length') ${ ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length')
and nuft(node, unit, f, t) and nuft(node, unit, f, t)
and (unit_investLP(unit) or unit_investMIP(unit)) and (unit_investLP(unit) or unit_investMIP(unit))
and not ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) and not sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
} .. } ..
+ v_reserve(restype, up_down, node, unit, s, f+df_reserves(node, restype, f, t), t) + v_reserve(restype, up_down, node, unit, s, f+df_reserves(node, restype, f, t), t)
...@@ -2111,8 +2124,10 @@ q_resTransferLimitLeftward(gn2n_directional(grid, node, node_), sft(s, f, t)) ...@@ -2111,8 +2124,10 @@ q_resTransferLimitLeftward(gn2n_directional(grid, node, node_), sft(s, f, t))
q_reserveProvisionRightward(restypeDirectionNodeNode(restype, up_down, node, node_), sft(s, f, t)) q_reserveProvisionRightward(restypeDirectionNodeNode(restype, up_down, node, node_), sft(s, f, t))
${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit')) ${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit'))
and sum(grid, gn2n_directional(grid, node, node_)) and sum(grid, gn2n_directional(grid, node, node_))
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
] ]
} .. } ..
...@@ -2141,9 +2156,11 @@ q_reserveProvisionRightward(restypeDirectionNodeNode(restype, up_down, node, nod ...@@ -2141,9 +2156,11 @@ q_reserveProvisionRightward(restypeDirectionNodeNode(restype, up_down, node, nod
q_reserveProvisionLeftward(restypeDirectionNodeNode(restype, up_down, node_, node), sft(s, f, t)) q_reserveProvisionLeftward(restypeDirectionNodeNode(restype, up_down, node_, node), sft(s, f, t))
${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit')) ${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit'))
and sum(grid, gn2n_directional(grid, node, node_)) and sum(grid, gn2n_directional(grid, node, node_))
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
] or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
]
} .. } ..
+ v_resTransferLeftward(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t) // df_reserves based on the receiving node + v_resTransferLeftward(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t) // df_reserves based on the receiving node
......
...@@ -476,22 +476,34 @@ df_reserves(node, restype, ft(f, t)) ...@@ -476,22 +476,34 @@ df_reserves(node, restype, ft(f, t))
and ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure') + p_nReserves(node, restype, 'update_frequency') - mod(tSolveFirst - 1 + p_nReserves(node, restype, 'gate_closure') + p_nReserves(node, restype, 'update_frequency') - p_nReserves(node, restype, 'update_offset'), p_nReserves(node, restype, 'update_frequency')) and ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure') + p_nReserves(node, restype, 'update_frequency') - mod(tSolveFirst - 1 + p_nReserves(node, restype, 'gate_closure') + p_nReserves(node, restype, 'update_frequency') - p_nReserves(node, restype, 'update_offset'), p_nReserves(node, restype, 'update_frequency'))
} }
= sum(f_${ mf_realization(mSolve, f_) }, ord(f_) - ord(f)) + Eps; // The Eps ensures that checks to see if df_reserves exists return positive even if the displacement is zero. = sum(f_${ mf_realization(mSolve, f_) }, ord(f_) - ord(f)) + Eps; // The Eps ensures that checks to see if df_reserves exists return positive even if the displacement is zero.
Option clear = df_reservesGroup;
df_reservesGroup(group, restype, ft(f, t))
${ p_groupReserves(group, restype, 'update_frequency')
and p_groupReserves(group, restype, 'gate_closure')
and ord(t) <= tSolveFirst + p_groupReserves(group, restype, 'gate_closure')
+ p_groupReserves(group, restype, 'update_frequency')
- mod(tSolveFirst - 1 + p_groupReserves(group, restype, 'gate_closure')
+ p_groupReserves(group, restype, 'update_frequency')
- p_groupReserves(group, restype, 'update_offset'),
p_groupReserves(group, restype, 'update_frequency'))
}
= sum(f_${ mf_realization(mSolve, f_) }, ord(f_) - ord(f)) + Eps; // The Eps ensures that checks to see if df_reserves exists return positive even if the displacement is zero.
// Set of ft-steps where the reserves are locked due to previous commitment // Set of ft-steps where the reserves are locked due to previous commitment
Option clear = ft_reservesFixed; Option clear = ft_reservesFixed;
ft_reservesFixed(node, restype, f_solve(f), t_active(t)) ft_reservesFixed(group, restype, f_solve(f), t_active(t))
${ mf_realization(mSolve, f) ${ mf_realization(mSolve, f)
and not tSolveFirst = mSettings(mSolve, 't_start') // No reserves are locked on the first solve! and not tSolveFirst = mSettings(mSolve, 't_start') // No reserves are locked on the first solve!
and p_nReserves(node, restype, 'update_frequency') and p_groupReserves(group, restype, 'update_frequency')
and p_nReserves(node, restype, 'gate_closure') and p_groupReserves(group, restype, 'gate_closure')
and ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure') and ord(t) <= tSolveFirst + p_groupReserves(group, restype, 'gate_closure')
+ p_nReserves(node, restype, 'update_frequency') + p_groupReserves(group, restype, 'update_frequency')
- mod(tSolveFirst - 1 - mod(tSolveFirst - 1
+ p_nReserves(node, restype, 'gate_closure') + p_groupReserves(group, restype, 'gate_closure')
- mSettings(mSolve, 't_jump') - mSettings(mSolve, 't_jump')
+ p_nReserves(node, restype, 'update_frequency') + p_groupReserves(group, restype, 'update_frequency')
- p_nReserves(node, restype, 'update_offset'), - p_groupReserves(group, restype, 'update_offset'),
p_nReserves(node, restype, 'update_frequency')) p_groupReserves(group, restype, 'update_frequency'))
- mSettings(mSolve, 't_jump') - mSettings(mSolve, 't_jump')
and not [ restypeReleasedForRealization(restype) // Free desired reserves for the to-be-realized time steps and not [ restypeReleasedForRealization(restype) // Free desired reserves for the to-be-realized time steps
and ft_realized(f, t) and ft_realized(f, t)
......
...@@ -102,12 +102,12 @@ $offtext ...@@ -102,12 +102,12 @@ $offtext
if (mTimeseries_loop_read(mSolve, 'ts_reserveDemand'), if (mTimeseries_loop_read(mSolve, 'ts_reserveDemand'),
put_utility 'gdxin' / '%input_dir%/ts_reserveDemand/' tSolve.tl:0 '.gdx'; put_utility 'gdxin' / '%input_dir%/ts_reserveDemand/' tSolve.tl:0 '.gdx';
execute_load ts_reserveDemand_update=ts_reserveDemand; execute_load ts_reserveDemand_update=ts_reserveDemand;
ts_reserveDemand(restypeDirectionNode(restype, up_down, node), f_solve(f), tt_forecast(t)) ts_reserveDemand(restypeDirectionGroup(restype, up_down, group), f_solve(f), tt_forecast(t))
${ not mf_realization(mSolve, f) // Realization not updated ${ not mf_realization(mSolve, f) // Realization not updated
and (mSettings(mSolve, 'onlyExistingForecasts') and (mSettings(mSolve, 'onlyExistingForecasts')
-> ts_reserveDemand_update(restype, up_down, node, f, t)) // Update only existing values (zeroes need to be EPS) -> ts_reserveDemand_update(restype, up_down, group, f, t)) // Update only existing values (zeroes need to be EPS)
} }
= ts_reserveDemand_update(restype, up_down, node, f, t); = ts_reserveDemand_update(restype, up_down, group, f, t);
); // END if('ts_reserveDemand') ); // END if('ts_reserveDemand')
// Update ts_node // Update ts_node
...@@ -194,8 +194,8 @@ $offtext ...@@ -194,8 +194,8 @@ $offtext
ts_cf(flowNode(flow, node), f, tt(t)) ts_cf(flowNode(flow, node), f, tt(t))
= ts_cf(flow, node, f, t) - ts_cf(flow, node, f+ddf(f), t); = ts_cf(flow, node, f, t) - ts_cf(flow, node, f+ddf(f), t);
// ts_reserveDemand // ts_reserveDemand