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 /
q_obj
q_balance
q_resDemand
q_resDemandLargestInfeedUnit
q_rateOfChangeOfFrequencyUnit
q_rateOfChangeOfFrequencyTransfer
q_resDemandLargestInfeedTransfer
q_resDemandLargestInfeedTransfer2
* q_resDemandLargestInfeedUnit
* q_rateOfChangeOfFrequencyUnit
* q_rateOfChangeOfFrequencyTransfer
* q_resDemandLargestInfeedTransfer
* q_resDemandLargestInfeedTransfer2
// Unit Operation
q_maxDownward
q_maxUpward
......
......@@ -56,6 +56,7 @@ Sets
flowNode(flow, node) "Nodes with flows"
* --- Sets bounding geography and units ---------------------------------------
group "A group of units, transfer links, nodes, etc."
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.
gn2n(grid, node, node) "All (directional) transfer links between nodes in specific energy grids"
......@@ -79,6 +80,8 @@ Sets
restypeDirection(restype, up_down) "Different combinations of reserve types and directions"
restypeDirectionNode(restype, up_down, node) "Nodes with reserve requirements"
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"
restypeReleasedForRealization(restype) "Reserve types that are released for the realized time intervals"
......@@ -102,7 +105,7 @@ Sets
ft(f, t) "Combination of forecasts and t:s in the current solve"
ft_realized(f, t) "Realized ft"
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"
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"
......@@ -156,7 +159,6 @@ $if defined scenario
shutdownCounter(unit, counter) "Counter used for unit shutdown intervals"
* --- 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"
gnuGroup(grid, node, unit, group) "Combination of grids, nodes and units in particular groups"
gn2nGroup(grid, node, node, group) "Transfer links in particular groups"
......
......@@ -52,6 +52,7 @@ Parameters
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_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_nuReserves(node, unit, restype, *) "Reserve provision data for units"
p_nnReserves(node, node, restype, up_down) "Reserve provision data for node node connections"
......@@ -126,6 +127,7 @@ Parameters
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_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"
// Sample displacement arrays
......@@ -151,7 +153,7 @@ Parameters
// Used mostly for raw data storage
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_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_fuelPriceChange(fuel, t) "Initial fuel price and consequent changes in fuel price (EUR/MWh)"
ts_fuelPrice(fuel, t) "Fuel price time series (EUR/MWh)"
......@@ -161,7 +163,7 @@ Parameters
// 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_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_fuelPrice_(fuel, t) "Mean fuel price time during time step (EUR/MWh)"
......@@ -171,7 +173,7 @@ Parameters
ts_effGroupUnit_update(effSelector, unit, param_eff, f, t)
ts_influx_update(grid, 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_fuelPriceChange_update(fuel, t)
ts_unavailability_update(unit, t)
......
......@@ -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)"
// 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_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)"
......@@ -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_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_qResDemand(restype, up_down, node, 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_nTotalqResDemand(restype, up_down, node) "Total dummy reserve provisions in n over the simulation"
r_qResDemand(restype, up_down, group, f, t) "Dummy to decrease demand for a reserve (MW) before 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, 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_solveStatus(t, solve_info) "Information about the solve"
......
......@@ -32,6 +32,7 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc unitUnitEffLevel
$$loaddc uFuel
$$loaddc effLevelGroupUnit
$$loaddc group
$$loaddc p_gn
$$loaddc p_gnn
$$loaddc p_gnu
......@@ -41,7 +42,7 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc restype
$$loaddc restypeDirection
$$loaddc restypeReleasedForRealization
$$loaddc p_nReserves
$$loaddc p_groupReserves
$$loaddc p_nReserves3D
$$loaddc p_nuReserves
$$loaddc p_nnReserves
......@@ -62,7 +63,6 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc ts_node
$$loaddc t_invest
$$loaddc p_storageValue
$$loaddc group
$$loaddc uGroup
$$loaddc gnuGroup
$$loaddc gn2nGroup
......@@ -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.
// 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
nuRescapable(restypeDirection(restype, up_down), nu(node, unit))
$ { p_nuReserves(node, unit, restype, up_down)
......@@ -423,6 +429,16 @@ restypeDirectionNode(restypeDirection(restype, up_down), node)
}
= 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 ------------------
// Reserve reliability assumed to be perfect if not provided in data
......@@ -547,11 +563,24 @@ loop( unitStarttype(unit, starttypeConstrained),
// Check that reserve_length is long enough for properly commitment of reserves
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'),
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!' /;
abort "The 'reserve_length' parameter should be longer than 'update_frequency' + 'gate_closure' to fix the reserves properly!"
); // 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)
// Check that reserve overlaps are possible
......
......@@ -56,8 +56,8 @@ 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)"
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_resMissing(restype, up_down, node, s, f, t) "Dummy to decrease demand for a reserve (MW) after 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, 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)"
;
......@@ -53,7 +53,7 @@ equations
// Objective Function, Energy Balance, and Reserve demand
q_obj "Objective function"
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_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"
......
......@@ -72,10 +72,10 @@ q_obj ..
) // END sum(inc_dec)
// Reserve provision feasibility dummy variable penalties
+ sum(restypeDirectionNode(restype, up_down, node),
+ vq_resDemand(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t)
+ sum(restypeDirectionGroup(restype, up_down, group),
+ vq_resDemand(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t)
* 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)
) // END sum(restypeDirectionNode)
......
......@@ -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! 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))
${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
q_resDemand(restypeDirectionGroup(restype, up_down, group), sft(s, f, t))
${ ord(t) < tSolveFirst + p_groupReserves(group, restype, 'reserve_length')
and not [ restypeReleasedForRealization(restype)
and sft_realized(s, f, t)]
} ..
// Reserve provision by capable units on this node
+ sum(nuft(node, unit, f, t)${nuRescapable(restype, up_down, node, unit)},
// Reserve provision by capable units on this group
+ 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)
* [ // 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.
+ p_nuReserves(node, unit, restype, 'reserveReliability')${not sft_realized(s, f+df_reserves(node, restype, f, t), t)}
] // END * v_reserve
) // END sum(nuft)
) // END sum(gnuft)
// 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)
* p_nuRes2Res(node, unit, restype_, up_down, restype)
* [ // Account for reliability of reserves
......@@ -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')
] // END * v_reserve
) // END sum(nuft)
) // END sum(gnuft)
// Reserve provision to this node via transfer links
+ sum(gn2n_directional(grid, node_, node)${restypeDirectionNodeNode(restype, up_down, node_, node)},
// Reserve provision to this group via transfer links
+ 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') )
* 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)
+ 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') )
* 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)
......@@ -129,26 +135,32 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), sft(s, f, t))
=G=
// Demand for reserves
+ ts_reserveDemand(restype, up_down, node, f, t)${p_nReserves(node, restype, 'use_time_series')}
+ p_nReserves(node, restype, up_down)${not p_nReserves(node, restype, 'use_time_series')}
+ ts_reserveDemand(restype, up_down, group, f, t)${p_groupReserves(group, restype, 'use_time_series')}
+ p_groupReserves(group, restype, up_down)${not p_groupReserves(group, restype, 'use_time_series')}
// 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...
* p_nuReserves(node, unit, restype, 'reserve_increase_ratio')
) // END sum(nuft)
// Reserve provisions to another nodes 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
// Reserve provisions to other groups via transfer links
+ 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)
) // 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)
) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables
- vq_resDemand(restype, up_down, node, s, f+df_reserves(node, 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_resDemand(restype, up_down, group, s, f+df_reservesGroup(group, 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 ----------------------------------------------------------
......@@ -208,8 +220,8 @@ q_resDemandLargestInfeedUnit(grid, restypeDirectionNode(restype, 'up', node), un
) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables
- 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_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)}
;
q_rateOfChangeOfFrequencyUnit(group, unit_fail(unit_), sft(s, f, t))
......@@ -366,8 +378,8 @@ q_resDemandLargestInfeedTransfer(grid, restypeDirectionNode(restype, 'up', node)
) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables
- 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_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)}
;
* --- 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
) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables
- 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_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)}
;
* --- 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
${ ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length')
and nuft(node, unit, f, t)
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)
......@@ -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))
${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit'))
and sum(grid, gn2n_directional(grid, node, node_))
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)
or 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))
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t))
]
} ..
......@@ -2141,8 +2156,10 @@ q_reserveProvisionRightward(restypeDirectionNodeNode(restype, up_down, node, nod
q_reserveProvisionLeftward(restypeDirectionNodeNode(restype, up_down, node_, node), sft(s, f, t))
${ sum(grid, p_gnn(grid, node, node_, 'transferCapInvLimit'))
and sum(grid, gn2n_directional(grid, node, node_))
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)
or 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))
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), 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'))
}
= 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
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)
and not tSolveFirst = mSettings(mSolve, 't_start') // No reserves are locked on the first solve!
and p_nReserves(node, restype, 'update_frequency')
and p_nReserves(node, restype, 'gate_closure')
and ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure')
+ p_nReserves(node, restype, 'update_frequency')
and 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_nReserves(node, restype, 'gate_closure')
+ p_groupReserves(group, restype, 'gate_closure')
- mSettings(mSolve, 't_jump')
+ p_nReserves(node, restype, 'update_frequency')
- p_nReserves(node, restype, 'update_offset'),
p_nReserves(node, restype, 'update_frequency'))
+ p_groupReserves(group, restype, 'update_frequency')
- p_groupReserves(group, restype, 'update_offset'),
p_groupReserves(group, restype, 'update_frequency'))
- mSettings(mSolve, 't_jump')
and not [ restypeReleasedForRealization(restype) // Free desired reserves for the to-be-realized time steps
and ft_realized(f, t)
......
......@@ -102,12 +102,12 @@ $offtext
if (mTimeseries_loop_read(mSolve, 'ts_reserveDemand'),
put_utility 'gdxin' / '%input_dir%/ts_reserveDemand/' tSolve.tl:0 '.gdx';
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
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')
// Update ts_node
......@@ -194,8 +194,8 @@ $offtext
ts_cf(flowNode(flow, node), f, tt(t))
= ts_cf(flow, node, f, t) - ts_cf(flow, node, f+ddf(f), t);
// ts_reserveDemand
ts_reserveDemand(restypeDirectionNode(restype, up_down, node), f, tt(t))
= ts_reserveDemand(restype, up_down, node, f, t) - ts_reserveDemand(restype, up_down, node, f+ddf(f), t);
ts_reserveDemand(restypeDirectionGroup(restype, up_down, group), f, tt(t))
= ts_reserveDemand(restype, up_down, group, f, t) - ts_reserveDemand(restype, up_down, group, f+ddf(f), t);
// ts_node
ts_node(gn(grid, node), param_gnBoundaryTypes, f, tt(t))
= ts_node(grid, node, param_gnBoundaryTypes, f, t) - ts_node(grid, node, param_gnBoundaryTypes, f+ddf(f), t);
......@@ -243,11 +243,11 @@ $offtext
* ts_cf(flow, node, f+ddf_(f), t)
] / mSettings(mSolve, 't_improveForecast');
// ts_reserveDemand
ts_reserveDemand(restypeDirectionNode(restype, up_down, node), f, tt(t))
ts_reserveDemand(restypeDirectionGroup(restype, up_down, group), f, tt(t))
= [ + (ord(t) - tSolveFirst)
* ts_reserveDemand(restype, up_down, node, f, t)
* ts_reserveDemand(restype, up_down, group, f, t)
+ (tSolveFirst - ord(t) + mSettings(mSolve, 't_improveForecast'))
* ts_reserveDemand(restype, up_down, node, f+ddf_(f), t)
* ts_reserveDemand(restype, up_down, group, f+ddf_(f), t)
] / mSettings(mSolve, 't_improveForecast');
// ts_node
ts_node(gn(grid, node), param_gnBoundaryTypes, f, tt(t))
......@@ -280,8 +280,8 @@ $offtext
ts_cf(flowNode(flow, node), f, tt(t))
= max(min(ts_cf(flow, node, f, t) + ts_cf(flow, node, f+ddf(f), t), 1), 0); // Ensure that capacity factor forecasts remain between 0-1
// ts_reserveDemand
ts_reserveDemand(restypeDirectionNode(restype, up_down, node), f, tt(t))
= max(ts_reserveDemand(restype, up_down, node, f, t) + ts_reserveDemand(restype, up_down, node, f+ddf(f), t), 0); // Ensure that reserve demand forecasts remains positive
ts_reserveDemand(restypeDirectionGroup(restype, up_down, group), f, tt(t))
= max(ts_reserveDemand(restype, up_down, group, f, t) + ts_reserveDemand(restype, up_down, group, f+ddf(f), t), 0); // Ensure that reserve demand forecasts remains positive
// ts_node
ts_node(gn(grid, node), param_gnBoundaryTypes, f, tt(t))
= ts_node(grid, node, param_gnBoundaryTypes, f, t) + ts_node(grid, node, param_gnBoundaryTypes, f+ddf(f), t);
......@@ -336,11 +336,11 @@ $offtext
)
/ mInterval(mSolve, 'stepsPerInterval', counter);
// Reserves relevant only until reserve_length
ts_reserveDemand_(restypeDirectionNode(restype, up_down, node), ft(f, tt_interval(t)))
${ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length') }
ts_reserveDemand_(restypeDirectionGroup(restype, up_down, group), ft(f, tt_interval(t)))
${ord(t) <= tSolveFirst + p_groupReserves(group, restype, 'reserve_length') }
= sum(tt_aggregate(t, t_),
ts_reserveDemand(restype, up_down, node,
f + (df_scenario(f, t)$gn_scenarios(restype, node, 'ts_reserveDemand')),
ts_reserveDemand(restype, up_down, group,
f + (df_scenario(f, t)${sum(gnGroup(grid, node, group), gn_scenarios(restype, node, 'ts_reserveDemand'))} ),
t_+ dt_circular(t_))
)
/ mInterval(mSolve, 'stepsPerInterval', counter);
......
......@@ -323,7 +323,9 @@ loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t)
v_reserve.up(nuRescapable(restype, up_down, node, unit), s, f+df_reserves(node, restype, f, t), t)
${ nuft(node, unit, f, t) // nuft is not displaced by df_reserves, as the unit exists on normal ft.
and not (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_reserves(node, restype, f, t), t)
)
}
= min ( p_nuReserves(node, unit, restype, up_down) * [ p_gnu(grid, node, unit, 'maxGen') + p_gnu(grid, node, unit, 'maxCons') ], // Generator + consuming unit res_range limit
v_gen.up(grid, node, unit, s, f, t) - v_gen.lo(grid, node, unit, s, f, t) // Generator + consuming unit available unit_elec. output delta
......@@ -334,8 +336,12 @@ loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t)
v_resTransferRightward.up(restypeDirectionNodeNode(restype, up_down, node, node_), s, f+df_reserves(node, restype, f, t), t)
${ not p_gnn(grid, node, node_, 'transferCapInvLimit')
and gn2n_directional(grid, node, node_)
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) // This set contains the combination of reserve types and time intervals that should be fixed
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) // Commit reserve transfer as long as either end commits.
and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
) // This set contains the combination of reserve types and time intervals that should be fixed
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reserves(node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= p_gnn(grid, node, node_, 'transferCap')
......@@ -344,8 +350,12 @@ loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t)
v_resTransferLeftward.up(restypeDirectionNodeNode(restype, up_down, node, node_), s, f+df_reserves(node, restype, f, t), t)
${ not p_gnn(grid, node, node_, 'transferCapInvLimit')
and gn2n_directional(grid, node, node_)
and not [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) // This set contains the combination of reserve types and time intervals that should be fixed
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) // Commit reserve transfer as long as either end commits.
and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
) // This set contains the combination of reserve types and time intervals that should be fixed
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reserves(node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= p_gnn(grid, node, node_, 'transferCap')
......@@ -353,7 +363,9 @@ loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t)
// Fix non-flow unit reserves at the gate closure of reserves
v_reserve.fx(nuRescapable(restype, up_down, node, unit), s, f+df_reserves(node, restype, f, t), t)
$ { ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) // This set contains the combination of reserve types and time intervals that should be fixed based on previous solves
$ { sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
) // This set contains the combination of reserve types and time intervals that should be fixed based on previous solves
and not unit_flow(unit) // NOTE! Units using flows can change their reserve (they might not have as much available in real time as they had bid)
}
= r_reserve(restype, up_down, node, unit, f+df_reserves(node, restype, f, t), t);
......@@ -361,24 +373,32 @@ loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t)
// Fix transfer of reserves at the gate closure of reserves, LOWER BOUND ONLY!
v_resTransferRightward.fx(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t)
$ { gn2n_directional(grid, node, node_)
and [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) // This set contains the combination of reserve types and time intervals that should be fixed
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) // Commit reserve transfer as long as either end commits.
and [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
) // This set contains the combination of reserve types and time intervals that should be fixed
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reserves(node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= r_resTransferRightward(restype, up_down, node, node_, f+df_reserves(node, restype, f, t), t);
v_resTransferLeftward.fx(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t)
$ { gn2n_directional(grid, node, node_)
and [ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) // This set contains the combination of reserve types and time intervals that should be fixed
or ft_reservesFixed(node_, restype, f+df_reserves(node_, restype, f, t), t) // Commit reserve transfer as long as either end commits.
and [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
) // This set contains the combination of reserve types and time intervals that should be fixed
or sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node_, group),
ft_reservesFixed(group, restype, f+df_reserves(node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= r_resTransferLeftward(restype, up_down, node, node_, f+df_reserves(node, restype, f, t), t);
// Fix slack variable for reserves that is used before the reserves need to be locked (vq_resMissing is used after this)
vq_resDemand.fx(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) } // This set contains the combination of reserve types and time intervals that should be fixed
= r_qResDemand(restype, up_down, node, f+df_reserves(node, restype, f, t), t);
vq_resDemand.fx(restype, up_down, group, s, f+df_reserves(node, restype, f, t), t)
$ { ft_reservesFixed(group, restype, f+df_reservesGroup(group, restype, f, t), t) } // This set contains the combination of reserve types and time intervals that should be fixed
= r_qResDemand(restype, up_down, group, f+df_reservesGroup(group, restype, f, t), t);
); // END loop(restypeDirectionNode, ft)
......
......@@ -97,11 +97,11 @@ loop((restypeDirectionNode(restype, up_down, node), sft(s, f, t))
= v_resTransferLeftward.l(restype, up_down, node, to_node, s, f+df_reserves(to_node, restype, f, t), t);
// Dummy reserve demand changes
r_qResDemand(restype, up_down, node, f+df_reserves(node, restype, f, t), t)
= vq_resDemand.l(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t);
r_qResDemand(restype, up_down, group, f+df_reservesGroup(group, restype, f, t), t)
= vq_resDemand.l(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t);
r_qResMissing(restype, up_down, node, f+df_reserves(node, restype, f, t), t)
= vq_resMissing.l(restype, up_down, node, s, f+df_reserves(node, restype, f, t), t);
r_qResMissing(restype, up_down, group, f+df_reservesGroup(group, restype, f, t), t)
= vq_resMissing.l(restype, up_down, group, s, f+df_reservesGroup(group, restype, f, t), t);
); // END loop(restypeDirectionNode, sft)
......@@ -137,8 +137,8 @@ loop(sft_realized(s, f, t),
= q_balance.m(grid, node, mSolve, s, f, t)
;
// q_resDemand marginal values
r_resDemandMarginal(restypeDirectionNode(restype, up_down, node), f, t)$[ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')]
= q_resDemand.m(restype, up_down, node, s, f, t)
r_resDemandMarginal(restypeDirectionGroup(restype, up_down, group), f, t)$[ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')]
= q_resDemand.m(restype, up_down, group, s, f, t)
;
// v_stateSlack values for calculation of realized costs later on
r_stateSlack(gn_stateSlack(grid, node), slack, f, t)$[ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')]
......
......@@ -260,9 +260,9 @@ loop(m,
); // END sum(ft_realizedNoReset)
// Total dummy reserve provisions over the simulation
r_nTotalqResDemand(restypeDirectionNode(restype, up_down, node))
r_nTotalqResDemand(restypeDirectionGroup(restype, up_down, group))
= sum(ft_realizedNoReset(f, t)$[ord(t) > mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod')],
+ r_qResDemand(restype, up_down, node, f, t)
+ r_qResDemand(restype, up_down, group, f, t)
* p_stepLengthNoReset(m, f, t)
* sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s))
); // END sum(ft_realizedNoReset)
......@@ -296,8 +296,8 @@ r_balanceMarginal(gn(grid, node), ft_realizedNoReset(f, t))$[ord(t) > mSettings(
= 1e6 * r_balanceMarginal(grid, node, f, t);
// Reserve balance
r_resDemandMarginal(restypeDirectionNode(restype, up_down, node), ft_realizedNoReset(f, t))$[ord(t) > mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod')]