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

Adding the grid dimension to the reserve parameters and variables. Changes...

Adding the grid dimension to the reserve parameters and variables. Changes required to the input files. Removing the obsolete nuft set. Issue #69.
parent 67f18153
......@@ -134,10 +134,11 @@ r_resTransferLeftward
// Interesting reserve results
r_resDemandMarginal
r_nuTotalReserve
r_nuTotalReserveShare
r_nTotalReserve
r_gnuTotalReserve
r_gnuTotalReserveShare
r_groupTotalReserve
r_resDemandLargestInfeedUnit
* --- Investment Result Symbols -----------------------------------------------
// Interesting investment results
......@@ -152,7 +153,7 @@ r_gnTotalqGen
r_gTotalqGen
r_qResDemand
r_qResMissing
r_nTotalqResDemand
r_groupTotalqResDemand
r_qCapacity
r_solveStatus
......
......@@ -78,11 +78,11 @@ Sets
* --- Reserve types -----------------------------------------------------------
restype "Reserve types"
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"
restypeDirectionGridNode(restype, up_down, grid, node) "Nodes with reserve requirements"
resTypeDirectionGridNodeNode(restype, up_down, grid, 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"
gnuRescapable(restype, up_down, grid, node, unit) "Units capable and available to provide particular reserves"
restypeReleasedForRealization(restype) "Reserve types that are released for the realized time intervals"
* --- Sets to define time, forecasts and samples ------------------------------
......@@ -143,7 +143,7 @@ $if defined scenario
uft_startupTrajectory(unit, f, t) "Units with start-up trajectories on intervals"
uft_shutdownTrajectory(unit, f, t) "Units with shutdown trajectories on intervals"
uft_aggregator_first(unit, f, t) "The first intervals when aggregator units are active"
nuft(node, unit, f, t) "Enables aggregation of nodes and units for later intervals"
* nuft(node, unit, f, t) "Enables aggregation of nodes and units for later intervals"
gnuft(grid, node, unit, f, t) "Enables aggregation of nodes and units for later intervals"
gnuft_ramp(grid, node, unit, f, t) "Units with ramp requirements or costs"
gnuft_rampCost(grid, node, unit, slack, f, t) "Units with ramp costs"
......
......@@ -51,13 +51,13 @@ Parameters
p_gnu(grid, node, unit, param_gnu) "Unit data 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_nReserves(node, restype, *) "Data defining the reserve rules in each node"
p_gnReserves(grid, node, restype, *) "Data defining the reserve rules in each node"
p_groupReserves(group, restype, *) "Data defining the reserve rules in each node group"
p_groupReserves3D(group, restype, up_down, param_policy) "Reserve policy in each node group separately for each reserve type and direction"
p_groupReserves4D(group, restype, up_down, group, param_policy) "Reserve policy in each node group separately for each reserve type and direction, also linking to another group"
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_nuRes2Res(node, unit, restype, up_down, restype) "The first type of reserve can be used also in the second reserve category (with a possible multiplier)"
p_gnuReserves(grid, node, unit, restype, *) "Reserve provision data for units"
p_gnnReserves(grid, node, node, restype, up_down) "Reserve provision data for node node connections"
p_gnuRes2Res(grid, node, unit, restype, up_down, restype) "The first type of reserve can be used also in the second reserve category (with a possible multiplier)"
p_gnPolicy(grid, node, param_policy, *) "Policy data for grid, node"
p_groupPolicy(group, param_policy) "Two-dimensional policy data for groups"
p_groupPolicy3D(group, param_policy, *) "Three-dimensional policy data for groups"
......@@ -127,7 +127,7 @@ Parameters
// Forecast displacement arrays
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_reserves(grid, node, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves"
df_reservesGroup(group, restype, f, t) "Forecast index displacement needed to reach the realized forecast when committing reserves"
df_scenario(f, t) "Forecast index displacement needed to get central forecast data for long-term scenarios"
......
......@@ -130,17 +130,17 @@ Parameters
* --- Reserve Provision Results -----------------------------------------------
// Reserve provision results required for model structure
r_reserve(restype, up_down, node, unit, f, t) "Unit capacity reserved for providing reserve of specific type (MW)"
r_resTransferRightward(restype, up_down, node, node, f, t) "Electricity transmission capacity from the first node to the second node reserved for providing reserves (MW)"
r_resTransferLeftward(restype, up_down, node, node, f, t) "Electricity transmission capacity from the second node to the first node reserved for providing reserves (MW)"
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_reserve(restype, up_down, grid, node, unit, f, t) "Unit capacity reserved for providing reserve of specific type (MW)"
r_resTransferRightward(restype, up_down, grid, node, node, f, t) "Electricity transmission capacity from the first node to the second node reserved for providing reserves (MW)"
r_resTransferLeftward(restype, up_down, grid, node, node, f, t) "Electricity transmission capacity from the second node to the first node reserved for providing reserves (MW)"
r_reserve2Reserve(restype, up_down, grid, 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, 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)"
r_resDemandLargestInfeedUnit(grid, f, t, restype, up_down, node) "Reserve Demand from the loss of largest infeed unit"
r_gnuTotalReserve(restype, up_down, grid, node, unit) "Total gnu reserve provision over the simulation (MW*h)"
r_gnuTotalReserveShare(restype, up_down, grid, node, unit) "Total gnu/group reserve provision share over the simulation"
r_groupTotalReserve(restype, up_down, group) "Total reserve provisions in groups over the simulation (MW*h)"
r_resDemandLargestInfeedUnit(restype, up_down, group, f, t) "Reserve Demand from the loss of largest infeed unit"
* --- Investment Results ------------------------------------------------------
......@@ -156,7 +156,7 @@ Parameters
r_gTotalqGen(inc_dec, grid) "Total dummy energy generation/consumption in g over the simulation (MWh)."
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_groupTotalqResDemand(restype, up_down, group) "Total dummy reserve provisions in the group 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"
......
......@@ -45,9 +45,9 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc p_groupReserves
$$loaddc p_groupReserves3D
$$loaddc p_groupReserves4D
$$loaddc p_nuReserves
$$loaddc p_nnReserves
$$loaddc p_nuRes2Res
$$loaddc p_gnuReserves
$$loaddc p_gnnReserves
$$loaddc p_gnuRes2Res
$$loaddc ts_reserveDemand
$$loaddc p_gnBoundaryPropertiesForStates
$$loaddc p_gnPolicy
......@@ -94,7 +94,7 @@ gngnu_constrainedOutputRatio
restype
restypeReleasedForRealization
p_gnn
p_nnReserves
p_gnnReserves
p_gnuBoundaryProperties
ts_node
ts_reserveDemand
......@@ -402,31 +402,31 @@ 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
// Copy data from p_groupReserves to p_gnReserves
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);
p_gnReserves(grid, node, restype, param_policy) = p_groupReserves(group, restype, param_policy);
p_gnReserves(grid, 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)
gnuRescapable(restypeDirection(restype, up_down), gnu(grid, node, unit))
$ { p_gnuReserves(grid, node, unit, restype, up_down)
}
= yes;
// Node-node connections with reserve transfer capabilities
restypeDirectionNodeNode(restypeDirection(restype, up_down), node, node_)
$ { p_nnReserves(node, node_, restype, up_down)
restypeDirectionGridNodeNode(restypeDirection(restype, up_down), gn2n(grid, node, node_))
$ { p_gnnReserves(grid, node, node_, restype, up_down)
}
= yes;
// Nodes with reserve requirements, units capable of providing reserves, or reserve capable connections
restypeDirectionNode(restypeDirection(restype, up_down), node)
$ { p_nReserves(node, restype, up_down)
or p_nReserves(node, restype, 'use_time_series')
or sum(nu(node, unit), p_nuReserves(node, unit, restype, 'portion_of_infeed_to_reserve'))
or sum(nu(node, unit), nuRescapable(restype, up_down, node, unit))
or sum(gn2n(grid, node, to_node), restypeDirectionNodeNode(restype, up_down, node, to_node))
restypeDirectionGridNode(restypeDirection(restype, up_down), gn(grid, node))
$ { p_gnReserves(grid, node, restype, up_down)
or p_gnReserves(grid, node, restype, 'use_time_series')
or sum(gnu(grid, node, unit), p_gnuReserves(grid, node, unit, restype, 'portion_of_infeed_to_reserve'))
or sum(gnu(grid, node, unit), gnuRescapable(restype, up_down, grid, node, unit))
or sum(gn2n(grid, node, to_node), restypeDirectionGridNodeNode(restype, up_down, grid, node, to_node))
}
= yes;
......@@ -443,19 +443,19 @@ restypeDirectionGridNodeGroup(restypeDirection(restype, up_down), gnGroup(grid,
* --- Correct values for critical reserve related parameters ------------------
// Reserve reliability assumed to be perfect if not provided in data
p_nuReserves(nu(node, unit), restype, 'reserveReliability')
${ not p_nuReserves(node, unit, restype, 'reserveReliability')
and sum(up_down, nuRescapable(restype, up_down, node, unit))
p_gnuReserves(gnu(grid, node, unit), restype, 'reserveReliability')
${ not p_gnuReserves(grid, node, unit, restype, 'reserveReliability')
and sum(up_down, gnuRescapable(restype, up_down, grid, node, unit))
}
= 1;
// Reserve provision overlap decreases the capacity of the overlapping category
p_nuReserves(nu(node, unit), restype, up_down)
${ nuRescapable(restype, up_down, node, unit) }
= p_nuReserves(node, unit, restype, up_down)
- sum(restype_${ p_nuRes2Res(node, unit, restype_, up_down, restype) },
+ p_nuReserves(node, unit, restype_, up_down)
* p_nuRes2Res(node, unit, restype_, up_down, restype)
p_gnuReserves(gnu(grid, node, unit), restype, up_down)
${ gnuRescapable(restype, up_down, grid, node, unit) }
= p_gnuReserves(grid, node, unit, restype, up_down)
- sum(restype_${ p_gnuRes2Res(grid, node, unit, restype_, up_down, restype) },
+ p_gnuReserves(grid, node, unit, restype_, up_down)
* p_gnuRes2Res(grid, node, unit, restype_, up_down, restype)
); // END sum(restype_)
* =============================================================================
......@@ -563,9 +563,9 @@ loop( unitStarttype(unit, starttypeConstrained),
* --- Check reserve related data ----------------------------------------------
// Check that reserve_length is long enough for properly commitment of reserves
loop( restypeDirectionNode(restype, up_down, node),
loop( restypeDirectionGridNode(restype, up_down, grid, 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_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 /;
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!"
......@@ -585,13 +585,13 @@ loop( restypeDirectionNode(restype, up_down, node),
); // END loop(restypeDirectionNode)
// Check that reserve overlaps are possible
loop( (nu(node, unit), restypeDirection(restype, up_down)),
if( p_nuReserves(node, unit, restype, up_down) < 0,
loop( (gnu(grid, node, unit), restypeDirection(restype, up_down)),
if( p_gnuReserves(grid, node, unit, restype, up_down) < 0,
put log '!!! Error occurred on unit ', unit.tl:0 /;
put log '!!! Abort: Overlapping reserve capacities in p_nuRes2Res can result in excess reserve production!' /;
abort "Overlapping reserve capacities in p_nuRes2Res can result in excess reserve production!"
); // END if(p_nuReserves)
); // END loop((nu,restypeDirection))
put log '!!! Abort: Overlapping reserve capacities in p_gnuRes2Res can result in excess reserve production!' /;
abort "Overlapping reserve capacities in p_gnuRes2Res can result in excess reserve production!"
); // END if(p_gnuReserves)
); // END loop((gnu,restypeDirection))
* =============================================================================
......
......@@ -43,9 +43,9 @@ Positive variables
v_spill(grid, node, s, f, t) "Spill of energy from storage node during an interval (MWh)"
v_transferRightward(grid, node, node, s, f, t) "Average electricity transmission level from the first node to the second node during an interval (MW)"
v_transferLeftward(grid, node, node, s, f, t) "Average electricity transmission level from the second node to the first node during an interval (MW)"
v_resTransferRightward(restype, up_down, node, node, s, f, t) "Electricity transmission capacity from the first node to the second node reserved for providing reserves (MW)"
v_resTransferLeftward(restype, up_down, node, node, s, f, t) "Electricity transmission capacity from the second node to the first node reserved for providing reserves (MW)"
v_reserve(restype, up_down, node, unit, s, f, t) "Unit capacity reserved for providing reserve of specific type (MW)"
v_resTransferRightward(restype, up_down, grid, node, node, s, f, t) "Electricity transmission capacity from the first node to the second node reserved for providing reserves (MW)"
v_resTransferLeftward(restype, up_down, grid, node, node, s, f, t) "Electricity transmission capacity from the second node to the first node reserved for providing reserves (MW)"
v_reserve(restype, up_down, grid, node, unit, s, f, t) "Unit capacity reserved for providing reserve of specific type (MW)"
v_investTransfer_LP(grid, node, node, t) "Invested transfer capacity (MW)"
v_online_LP(unit, s, f, t) "Number of sub-units online for 'units' with unit commitment restrictions (LP variant)"
v_invest_LP(unit, t) "Number of invested 'sub-units' (LP variant)"
......
......@@ -63,7 +63,7 @@ equations
// Unit Operation
q_maxDownward(grid, node, unit, mType, s, f, t) "Downward commitments will not undercut power plant minimum load constraints or maximum elec. consumption"
q_maxUpward(grid, node, unit, mType, s, f, t) "Upward commitments will not exceed maximum available capacity or consumed power"
q_reserveProvision(restype, up_down, node, unit, s, f, t) "Reserve provision limited for units"
q_reserveProvision(restype, up_down, grid, node, unit, s, f, t) "Reserve provision limited for units"
q_startshut(mType, s, unit, f, t) "Online capacity now minus online capacity in the previous interval is equal to started up minus shut down capacity"
q_startuptype(mType, s, starttype, unit, f, t) "Startup type depends on the time the unit has been non-operational"
q_onlineOnStartUp(s, unit, f, t) "Unit must be online after starting up"
......@@ -95,8 +95,8 @@ equations
q_transferLeftwardLimit(grid, node, node, s, f, t) "Transfer of energy and capacity reservations to the leftward direction are less than the transfer capacity"
q_resTransferLimitRightward(grid, node, node, s, f, t) "Transfer of energy and capacity reservations are less than the transfer capacity to the rightward direction"
q_resTransferLimitLeftward(grid, node, node, s, f, t) "Transfer of energy and capacity reservations are less than the transfer capacity to the leftward direction"
q_reserveProvisionRightward(restype, up_down, node, node, s, f, t) "Rightward reserve provision limited"
q_reserveProvisionLeftward(restype, up_down, node, node, s, f, t) "Leftward reserve provision limited"
q_reserveProvisionRightward(restype, up_down, grid, node, node, s, f, t) "Rightward reserve provision limited"
q_reserveProvisionLeftward(restype, up_down, grid, node, node, s, f, t) "Leftward reserve provision limited"
// State Variables
q_stateSlack(grid, node, slack, s, f, t) "Slack variable greater than the difference between v_state and the slack boundary"
......
This diff is collapsed.
......@@ -580,19 +580,19 @@ loop(unit,
loop(m,
// Disable node reserve requirements
restypeDirectionNode(restype, up_down, node)
restypeDirectionGridNode(restype, up_down, grid, node)
${ not mSettingsReservesInUse(m, restype, up_down)
}
= no;
// Disable node-node reserve connections
restypeDirectionNodeNode(restype, up_down, node, node_)
restypeDirectionGridNodeNode(restype, up_down, grid, node, node_)
${ not mSettingsReservesInUse(m, restype, up_down)
}
= no;
// Disable reserve provision capability from units
nuRescapable(restype, up_down, node, unit)
gnuRescapable(restype, up_down, grid, node, unit)
${ not mSettingsReservesInUse(m, restype, up_down)
}
= no;
......@@ -621,7 +621,9 @@ p_slackDirection(downwardSlack) = -1;
* --- Using default value for reserves update frequency -----------------------
loop(m,
p_nReserves(node, restype, 'update_frequency')${ not p_nReserves(node, restype, 'update_frequency') }
p_groupReserves(group, restype, 'update_frequency')${ not p_groupReserves(group, restype, 'update_frequency') }
= mSettings(m, 't_jump');
p_gnReserves(grid, node, restype, 'update_frequency')${ not p_gnReserves(grid, node, restype, 'update_frequency') }
= mSettings(m, 't_jump');
);
......@@ -654,28 +656,28 @@ loop(m, // Not ideal, but multi-model functionality is not yet implemented
* --- Reserve structure checks ------------------------------------------------
loop(restypeDirectionNode(restype, up_down, node),
loop(restypeDirectionGroup(restype, up_down, group),
// Check that 'update_frequency' is longer than 't_jump'
if(p_nReserves(node, restype, 'update_frequency') < mSettings(m, 't_jump'),
put log '!!! Error occurred on p_nReserves ' node.tl:0 ',' restype.tl:0 /;
if(p_groupReserves(group, restype, 'update_frequency') < mSettings(m, 't_jump'),
put log '!!! Error occurred on p_groupReserves ' group.tl:0 ',' restype.tl:0 /;
put log '!!! Abort: The update_frequency parameter should be longer than or equal to t_jump!' /;
abort "The 'update_frequency' parameter should be longer than or equal to 't_jump'!";
); // END if('update_frequency' < 't_jump')
// Check that 'update_frequency' is divisible by 't_jump'
if(mod(p_nReserves(node, restype, 'update_frequency'), mSettings(m, 't_jump')) <> 0,
put log '!!! Error occurred on p_nReserves ' node.tl:0 ',' restype.tl:0 /;
if(mod(p_groupReserves(group, restype, 'update_frequency'), mSettings(m, 't_jump')) <> 0,
put log '!!! Error occurred on p_groupReserves ' group.tl:0 ',' restype.tl:0 /;
put log '!!! Abort: The update_frequency parameter should be divisible by t_jump!' /;
abort "The 'update_frequency' parameter should be divisible by 't_jump'!";
); // END if(mod('update_frequency'))
// Check if the first interval is long enough for proper commitment of reserves
if(mInterval(m, 'lastStepInIntervalBlock', 'c000') < p_nReserves(node, restype, 'update_frequency') + p_nReserves(node, restype, 'gate_closure'),
put log '!!! Error occurred on p_nReserves ' node.tl:0 ',' restype.tl:0 /;
if(mInterval(m, 'lastStepInIntervalBlock', 'c000') < p_groupReserves(group, restype, 'update_frequency') + p_groupReserves(group, restype, 'gate_closure'),
put log '!!! Error occurred on p_groupReserves ' group.tl:0 ',' restype.tl:0 /;
put log '!!! Abort: The first interval block should not be shorter than update_frequency + gate_closure for proper commitment of reserves!' /;
abort "The first interval block should not be shorter than 'update_frequency' + 'gate_closure' for proper commitment of reserves!";
); // END if
); // END loop(restypeDirectionNode)
); // END loop(restypeDirectionGroup)
* --- Check that there aren't more effLevels defined than exist in data -------
......
......@@ -472,10 +472,15 @@ df_central(ft(f,t))${ ord(t) > tSolveFirst + currentForecastLength - p_stepLen
// Forecast index displacement between realized and forecasted intervals, required for locking reserves ahead of (dispatch) time.
Option clear = df_reserves;
df_reserves(node, restype, ft(f, t))
${ 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') - 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'))
df_reserves(grid, node, restype, ft(f, t))
${ p_gnReserves(grid, node, restype, 'update_frequency')
and p_gnReserves(grid, node, restype, 'gate_closure')
and ord(t) <= tSolveFirst + p_gnReserves(grid, node, restype, 'gate_closure')
+ p_gnReserves(grid, node, restype, 'update_frequency')
- mod(tSolveFirst - 1 + p_gnReserves(grid, node, restype, 'gate_closure')
+ p_gnReserves(grid, node, restype, 'update_frequency')
- p_gnReserves(grid, node, restype, 'update_offset'),
p_gnReserves(grid, 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;
......@@ -554,14 +559,9 @@ loop(unit${unit_aggregator(unit)},
uft_aggregator_first(uft(unit, f, t))${ord(t) = tmp} = yes;
);
// Active units in nodes on each ft
Option clear = nuft;
nuft(nu(node, unit), ft(f, t))${ uft(unit, f, t) }
= yes
;
// Active (grid, node, unit) on each ft
Option clear = gnuft;
gnuft(gn(grid, node), uft(unit, f, t))${ nuft(node, unit, f, t) }
gnuft(gn(grid, node), uft(unit, f, t))${ gnu(grid, node, unit) }
= yes
;
// Active (grid, node, unit, slack, up_down) on each ft step with ramp restrictions
......
......@@ -316,91 +316,90 @@ v_transferLeftward.up(gn2n_directional(grid, node, node_), sft(s, f, t))${ not
* --- Reserve Provision Boundaries --------------------------------------------
// Loop over the forecasts to minimize confusion regarding the df_reserves forecast displacement
// NOTE! The loop over gn is not ideal, but the reserve variables are currently lacking the grid dimension.
loop((restypeDirectionNode(restype, up_down, node), gn(grid, node), sft(s, f, t))${ ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length') },
loop((restypeDirectionGridNode(restype, up_down, grid, node), sft(s, f, t))${ ord(t) <= tSolveFirst + p_gnReserves(grid, node, restype, 'reserve_length') },
// Reserve provision limits without investments
// Reserve provision limits based on resXX_range (or possibly available generation in case of unit_flow)
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.
v_reserve.up(gnuRescapable(restype, up_down, grid, node, unit), s, f+df_reserves(grid, node, restype, f, t), t)
${ gnuft(grid, node, unit, f, t) // gnuft is not displaced by df_reserves, as the unit exists on normal ft.
and not (unit_investLP(unit) or unit_investMIP(unit))
and not sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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
= min ( p_gnuReserves(grid, 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
) // END min
;
// Reserve transfer upper bounds based on input p_nnReserves data, if investments are disabled
v_resTransferRightward.up(restypeDirectionNodeNode(restype, up_down, node, node_), s, f+df_reserves(node, restype, f, t), t)
v_resTransferRightward.up(restypeDirectionGridNodeNode(restype, up_down, grid, node, node_), s, f+df_reserves(grid, node, restype, f, t), t)
${ not p_gnn(grid, node, node_, 'transferCapInvLimit')
and gn2n_directional(grid, node, node_)
and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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)
ft_reservesFixed(group, restype, f+df_reserves(grid, node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= p_gnn(grid, node, node_, 'transferCap')
* p_nnReserves(node, node_, restype, up_down);
* p_gnnReserves(grid, node, node_, restype, up_down);
v_resTransferLeftward.up(restypeDirectionNodeNode(restype, up_down, node, node_), s, f+df_reserves(node, restype, f, t), t)
v_resTransferLeftward.up(restypeDirectionGridNodeNode(restype, up_down, grid, node, node_), s, f+df_reserves(grid, node, restype, f, t), t)
${ not p_gnn(grid, node, node_, 'transferCapInvLimit')
and gn2n_directional(grid, node, node_)
and not [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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)
ft_reservesFixed(group, restype, f+df_reserves(grid, node_, restype, f, t), t)
) // Commit reserve transfer as long as either end commits.
]
}
= p_gnn(grid, node, node_, 'transferCap')
* p_nnReserves(node, node_, restype, up_down);
* p_gnnReserves(grid, node, node_, restype, up_down);
// 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)
v_reserve.fx(gnuRescapable(restype, up_down, grid, node, unit), s, f+df_reserves(grid, node, restype, f, t), t)
$ { sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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);
= r_reserve(restype, up_down, grid, node, unit, f+df_reserves(grid, node, restype, f, t), 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)
v_resTransferRightward.fx(restype, up_down, grid, node, node_, s, f+df_reserves(grid, node, restype, f, t), t)
$ { gn2n_directional(grid, node, node_)
and [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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);
= r_resTransferRightward(restype, up_down, grid, node, node_, f+df_reserves(grid, node, restype, f, t), t);
v_resTransferLeftward.fx(restype, up_down, node, node_, s, f+df_reserves(node, restype, f, t), t)
v_resTransferLeftward.fx(restype, up_down, grid, node, node_, s, f+df_reserves(grid, node, restype, f, t), t)
$ { gn2n_directional(grid, node, node_)
and [ sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group),
ft_reservesFixed(group, restype, f+df_reserves(node, restype, f, t), t)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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)
ft_reservesFixed(group, restype, f+df_reserves(grid, 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);
= r_resTransferLeftward(restype, up_down, grid, node, node_, f+df_reserves(grid, 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, group, s, f+df_reserves(node, restype, f, t), t)
vq_resDemand.fx(restype, up_down, group, s, f+df_reserves(grid, 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)
); // END loop(restypeDirectionGridNode, ft)
* --- Investment Variable Boundaries ------------------------------------------
......
......@@ -61,40 +61,44 @@ loop(sft_realized(s, f, t),
* --- Reserve results ---------------------------------------------------------
// Loop over reserve horizon, as the reserve variables use a different ft-structure due to commitment
loop((restypeDirectionNode(restype, up_down, node), sft(s, f, t))
${ ord(t) <= tSolveFirst + p_nReserves(node, restype, 'reserve_length')
loop((restypeDirectionGridNode(restype, up_down, grid, node), sft(s, f, t))
${ ord(t) <= tSolveFirst + p_gnReserves(grid, node, restype, 'reserve_length')
and ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')
},
// Reserve provisions of units
r_reserve(nuRescapable(restype, up_down, node, unit), f+df_reserves(node, restype, f, t), t)
r_reserve(gnuRescapable(restype, up_down, grid, node, unit), f+df_reserves(grid, node, restype, f, t), t)
${ not [ restypeReleasedForRealization(restype)
and sft_realized(s, f+df_reserves(node, restype, f, t), t)
and sft_realized(s, f+df_reserves(grid, node, restype, f, t), t)
]
}
= + v_reserve.l(restype, up_down, node, unit, s, f+df_reserves(node, restype, f, t), t)
+ sum(restype_$p_nuRes2Res(node, unit, restype_, up_down, restype),
+ v_reserve.l(restype_, up_down, node, unit, s, f+df_reserves(node, restype_, f, t), t)
* p_nuRes2Res(node, unit, restype_, up_down, restype)
= + v_reserve.l(restype, up_down, grid, node, unit, s, f+df_reserves(grid, node, restype, f, t), t)
+ sum(restype_$p_gnuRes2Res(grid, node, unit, restype_, up_down, restype),
+ v_reserve.l(restype_, up_down, grid, node, unit, s, f+df_reserves(grid, node, restype_, f, t), t)
* p_gnuRes2Res(grid, node, unit, restype_, up_down, restype)
);
// Reserve requirement due to N-1 reserve constraint
r_resDemandLargestInfeedUnit(grid, f+df_reserves(node, restype, f, t), t, restype, up_down, node)
${ sum(unit_fail, nuRescapable(restype, up_down, node, unit_fail)) } // Calculate only for nodes with units that can fail.
= smax(unit_fail(unit_), v_gen.l(grid, node, unit_, s, f, t) * p_nuReserves(node, unit_, restype, 'portion_of_infeed_to_reserve'));
r_resDemandLargestInfeedUnit(restype, up_down, group, f+df_reserves(grid, node, restype, f, t), t)
${ sum((gnGroup(grid, node, group), unit_fail), gnuRescapable(restype, up_down, grid, node, unit_fail)) } // Calculate only for groups with units that can fail.
= smax(unit_fail(unit_),
+ v_gen.l(grid, node, unit_, s, f, t)
* p_gnuReserves(grid, node, unit_, restype, 'portion_of_infeed_to_reserve')
) // END smax(unit_fail)
;
// Reserve transfer capacity for links defined out from this node
r_resTransferRightward(restype, up_down, node, to_node, f+df_reserves(node, restype, f, t), t)
${ sum(grid, gn2n_directional(grid, node, to_node))
and restypeDirectionNodeNode(restype, up_down, node, to_node)
r_resTransferRightward(restype, up_down, grid, node, to_node, f+df_reserves(grid, node, restype, f, t), t)
${ gn2n_directional(grid, node, to_node)
and restypeDirectionGridNodeNode(restype, up_down, grid, node, to_node)
}
= v_resTransferRightward.l(restype, up_down, node, to_node, s, f+df_reserves(node, restype, f, t), t);
= v_resTransferRightward.l(restype, up_down, grid, node, to_node, s, f+df_reserves(grid, node, restype, f, t), t);
r_resTransferLeftward(restype, up_down, node, to_node, f+df_reserves(to_node, restype, f, t), t)
${ sum(grid, gn2n_directional(grid, node, to_node))
and restypeDirectionNodeNode(restype, up_down, to_node, node)
r_resTransferLeftward(restype, up_down, grid, node, to_node, f+df_reserves(grid, to_node, restype, f, t), t)
${ gn2n_directional(grid, node, to_node)
and restypeDirectionGridNodeNode(restype, up_down, grid, to_node, node)
}
= v_resTransferLeftward.l(restype, up_down, node, to_node, s, f+df_reserves(to_node, restype, f, t), t);
= v_resTransferLeftward.l(restype, up_down, grid, node, to_node, s, f+df_reserves(grid, to_node, restype, f, t), t);
// Dummy reserve demand changes
r_qResDemand(restype, up_down, group, f+df_reservesGroup(group, restype, f, t), t)
......
......@@ -252,15 +252,15 @@ loop(m,
* --- Total Reserve Provision -------------------------------------------------
// Total reserve provisions over the simulation
r_nuTotalReserve(nuRescapable(restype, up_down, node, unit))
r_gnuTotalReserve(gnuRescapable(restype, up_down, grid, node, unit))
= sum(ft_realizedNoReset(f, t)$[ord(t) > mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod')],
+ r_reserve(restype, up_down, node, unit, f, t)
+ r_reserve(restype, up_down, grid, node, unit, f, t)
* p_stepLengthNoReset(m, f, t)
* sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s))
); // END sum(ft_realizedNoReset)
// Total dummy reserve provisions over the simulation
r_nTotalqResDemand(restypeDirectionGroup(restype, up_down, group))
r_groupTotalqResDemand(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, group, f, t)
* p_stepLengthNoReset(m, f, t)
......@@ -447,20 +447,23 @@ r_totalRealizedNetCost
* --- Reserve Provision Overlap Results ---------------------------------------
// Calculate the overlapping reserve provisions
r_reserve2Reserve(nuRescapable(restype, up_down, node, unit), restype_, ft_realizedNoReset(f, t))
${ p_nuRes2Res(node, unit, restype, up_down, restype_) }
= r_reserve(restype, up_down, node, unit, f, t)
* p_nuRes2Res(node, unit, restype, up_down, restype_);
r_reserve2Reserve(gnuRescapable(restype, up_down, grid, node, unit), restype_, ft_realizedNoReset(f, t))
${ p_gnuRes2Res(grid, node, unit, restype, up_down, restype_) }
= r_reserve(restype, up_down, grid, node, unit, f, t)
* p_gnuRes2Res(grid, node, unit, restype, up_down, restype_);
* --- Total Reserve Provision Results -----------------------------------------
// Total reserve provision in nodes over the simulation
r_nTotalReserve(restypeDirectionNode(restype, up_down, node))
= sum(nuRescapable(restype, up_down, node, unit), r_nuTotalReserve(restype, up_down, node, unit));
// Total reserve provision in groups over the simulation
r_groupTotalReserve(restypeDirectionGroup(restype, up_down, group))
= sum(gnuRescapable(restype, up_down, grid, node, unit)${gnGroup(grid, node, group)},
+ r_gnuTotalReserve(restype, up_down, grid, node, unit)
); // END sum(gnuRescapable)
r_nuTotalReserveShare(nuRescapable(restype, up_down, node, unit))${ r_nTotalReserve(restype, up_down, node) > 0 }
= r_nuTotalReserve(restype, up_down, node, unit)
/ r_nTotalReserve(restype, up_down, node);
r_gnuTotalReserveShare(gnuRescapable(restype, up_down, grid, node, unit))
${ sum(gnGroup(grid, node, group), r_groupTotalReserve(restype, up_down, group)) > 0 }
= r_gnuTotalReserve(restype, up_down, grid, node, unit)
/ sum(gnGroup(grid, node, group), r_groupTotalReserve(restype, up_down, group));
* --- Total Unit Online State Results -----------------------------------------
......
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