Commit f00b174d authored by Topi Rasku's avatar Topi Rasku
Browse files

Reworking the reserves in the model.

Renaming "df_nReserves" to "df_reserves", changing "mft_nReserves" to "ft_reservesFixed", as well as a lot of accompanying changes.
parent 7d4d2271
......@@ -98,7 +98,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"
mft_nReserves(node, restype, mType, f, t) "Combination of forecasts and t:s locked due to committing reserves ahead of time."
ft_reservesFixed(node, 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 samples, forecasts and t:s in the current model solve"
......
......@@ -107,7 +107,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_nReserves(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"
// Temporary displacement arrays
ddt(t) "Temporary time displacement array"
......
......@@ -172,6 +172,7 @@ Option clear = r_startup;
Option clear = r_shutdown;
Option clear = r_invest;
Option clear = r_investTransfer;
Option clear = r_qResDemand;
* =============================================================================
* --- Diagnostics Results Arrays ----------------------------------------------
......
......@@ -72,9 +72,9 @@ q_obj ..
// Reserve provision feasibility dummy variable penalties
+ sum(restypeDirectionNode(restype, up_down, node),
+ vq_resDemand(restype, up_down, node, f, t)
+ vq_resDemand(restype, up_down, node, f+df_reserves(node, restype, f, t), t)${ not ft_reservesFixed(node, restype, f+df_reserves(node, restype,f, t), t) }
* PENALTY_RES(restype, up_down)
+ vq_resMissing(restype, up_down, node, f, t)$(ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1, p_nReserves(node, restype, 'update_frequency')))
+ vq_resMissing(restype, up_down, node, f+df_reserves(node, restype, f, t), t)${ ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t) }
* PENALTY_RES_MISSING(restype, up_down)
) // END sum(restypeDirectionNode)
......@@ -121,7 +121,7 @@ q_obj ..
+ sum(mft_start(m, f, t)${ p_storageValue(grid, node, t)
and active(m, 'storageValue')
},
+ v_state(grid, node, f, t)
+ v_state(grid, node, f+df_central(f,t), t)
* p_storageValue(grid, node, t)
* sum(ms(m, s)${ p_msft_probability(m, s, f, t) },
+ p_msft_probability(m, s, f, t)
......
......@@ -86,25 +86,28 @@ q_balance(gn(grid, node), mft(m, f, t))${ not p_gn(grid, node, 'boundAll')
;
* --- Reserve Demand ----------------------------------------------------------
// 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), ft(f, t)) ${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
and not [ restypeReleasedForRealization(restype)
and ft_realized(f, t)
]
} ..
q_resDemand(restypeDirectionNode(restype, up_down, node), ft(f, t))
${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
and not [ restypeReleasedForRealization(restype)
and ft_realized(f, t)
]
} ..
// Reserve provision by capable units on this node
+ sum(nuft(node, unit, f, t)${nuRescapable(restype, up_down, node, unit)},
+ v_reserve(restype, up_down, node, unit, f+df_nReserves(node, restype, f, t), t)
+ v_reserve(restype, up_down, node, unit, f+df_reserves(node, restype, f, t), t)
) // END sum(nuft)
// Reserve provision to this node via transfer links
+ sum(gn2n_directional(grid, node_, node)${restypeDirectionNodeNode(restype, up_down, node_, node)},
+ (1 - p_gnn(grid, node_, node, 'transferLoss') )
* v_resTransferRightward(restype, up_down, node_, node, f+df_nReserves(node_, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio') // Reserves from another node - reduces the need for reserves in the node
* v_resTransferRightward(restype, up_down, node_, node, f+df_reserves(node_, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio') // 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)},
+ (1 - p_gnn(grid, node, node_, 'transferLoss') )
* v_resTransferLeftward(restype, up_down, node, node_, f+df_nReserves(node_, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio') // Reserves from another node - reduces the need for reserves in the node
* v_resTransferLeftward(restype, up_down, node, node_, f+df_reserves(node_, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio') // Reserves from another node - reduces the need for reserves in the node
) // END sum(gn2n_directional)
=G=
......@@ -115,15 +118,15 @@ q_resDemand(restypeDirectionNode(restype, up_down, node), ft(f, t)) ${ ord(t) <
// 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
+ v_resTransferRightward(restype, up_down, node, node_, f+df_nReserves(node, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
+ v_resTransferRightward(restype, up_down, node, node_, f+df_reserves(node, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
) // 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
+ v_resTransferLeftward(restype, up_down, node_, node, f+df_nReserves(node, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
+ v_resTransferLeftward(restype, up_down, node_, node, f+df_reserves(node, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
) // END sum(gn2n_directional)
// Reserve demand feasibility dummy variables
- vq_resDemand(restype, up_down, node, f, t)
- vq_resMissing(restype, up_down, node, f, t)$(ord(t) <= tSolveFirst + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1, p_nReserves(node, restype, 'update_frequency')))
- vq_resDemand(restype, up_down, node, f+df_reserves(node, restype, f, t), t)${not ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
- vq_resMissing(restype, up_down, node, f+df_reserves(node, restype, f, t), t)${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
;
* --- Maximum Downward Capacity -----------------------------------------------
......@@ -155,7 +158,7 @@ q_maxDownward(m, gnuft(grid, node, unit, f, t))${ [ ord(t) < tSolveFirst + s
// Downward reserve participation
- sum(nuRescapable(restype, 'down', node, unit)${ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')},
+ v_reserve(restype, 'down', node, unit, f+df_nReserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
+ v_reserve(restype, 'down', node, unit, f+df_reserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
) // END sum(nuRescapable)
=G= // Must be greater than minimum load or maximum consumption (units with min-load and both generation and consumption are not allowed)
......@@ -270,7 +273,7 @@ q_maxUpward(m, gnuft(grid, node, unit, f, t))${ [ ord(t) < tSolveFirst + smax(
// Upwards reserve participation
+ sum(nuRescapable(restype, 'up', node, unit)${ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')},
+ v_reserve(restype, 'up', node, unit, f+df_nReserves(node, restype, f, t), t)
+ v_reserve(restype, 'up', node, unit, f+df_reserves(node, restype, f, t), t)
) // END sum(nuRescapable)
=L= // must be less than available/online capacity
......@@ -566,7 +569,7 @@ q_rampUpLimit(m, s, gnuft_ramp(grid, node, unit, f, t))${ ord(t) > msStart(m, s
} ..
+ v_genRamp(grid, node, unit, f, t)
+ sum(nuRescapable(restype, 'up', node, unit)${ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')},
+ v_reserve(restype, 'up', node, unit, f+df_nReserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
+ v_reserve(restype, 'up', node, unit, f+df_reserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
) // END sum(nuRescapable)
/ p_stepLength(m, f, t)
......@@ -630,7 +633,7 @@ q_rampDownLimit(m, s, gnuft_ramp(grid, node, unit, f, t))${ ord(t) > msStart(m,
} ..
+ v_genRamp(grid, node, unit, f, t)
- sum(nuRescapable(restype, 'down', node, unit)${ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')},
+ v_reserve(restype, 'down', node, unit, f+df_nReserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
+ v_reserve(restype, 'down', node, unit, f+df_reserves(node, restype, f, t), t) // (v_reserve can be used only if the unit is capable of providing a particular reserve)
) // END sum(nuRescapable)
/ p_stepLength(m, f, t)
......@@ -1114,10 +1117,10 @@ q_resTransferLimitRightward(gn2n_directional(grid, node, node_), ft(f, t))${
// Reserved transfer capacities from node
+ sum(restypeDirection(restype, 'up')${restypeDirectionNodeNode(restype, 'up', node_, node)},
+ v_resTransferRightward(restype, 'up', node, node_, f+df_nReserves(node_, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
+ v_resTransferRightward(restype, 'up', node, node_, f+df_reserves(node_, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
) // END sum(restypeDirection)
+ sum(restypeDirection(restype, 'down')${restypeDirectionNodeNode(restype, 'down', node, node_)},
+ v_resTransferLeftward(restype, 'down', node, node_, f+df_nReserves(node, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
+ v_resTransferLeftward(restype, 'down', node, node_, f+df_reserves(node, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
) // END sum(restypeDirection)
=L=
......@@ -1145,10 +1148,10 @@ q_resTransferLimitLeftward(gn2n_directional(grid, node, node_), ft(f, t))${ sum(
// Reserved transfer capacities from node
- sum(restypeDirection(restype, 'up')${restypeDirectionNodeNode(restype, 'up', node, node_)},
+ v_resTransferLeftward(restype, 'up', node, node_, f+df_nReserves(node, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
+ v_resTransferLeftward(restype, 'up', node, node_, f+df_reserves(node, restype, f, t), t) * p_nnReserves(node, node_, restype, 'ratio')
) // END sum(restypeDirection)
- sum(restypeDirection(restype, 'down')${restypeDirectionNodeNode(restype, 'down', node_, node)},
+ v_resTransferRightward(restype, 'down', node, node_, f+df_nReserves(node_, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
+ v_resTransferRightward(restype, 'down', node, node_, f+df_reserves(node_, restype, f, t), t) * p_nnReserves(node_, node, restype, 'ratio')
) // END sum(restypeDirection)
=G=
......@@ -1232,7 +1235,7 @@ q_stateUpwardLimit(gn_state(grid, node), mft(m, f, t))${ sum(gn2gnu(grid, nod
+ sum(gn2gnu(grid_, node_input, grid, node, unit)${uft(unit, f, t)},
// Downward reserves from units that output energy to the node
+ sum(nuRescapable(restype, 'down', node_input, unit)${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length') },
+ v_reserve(restype, 'down', node_input, unit, f+df_nReserves(node_input, restype, f, t), t)
+ v_reserve(restype, 'down', node_input, unit, f+df_reserves(node_input, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1244,7 +1247,7 @@ q_stateUpwardLimit(gn_state(grid, node), mft(m, f, t))${ sum(gn2gnu(grid, nod
+ sum(gn2gnu(grid, node, grid_, node_output, unit)${uft(unit, f, t)},
// Downward reserves from units that use the node as energy input
+ sum(nuRescapable(restype, 'down', node_output, unit)${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length') },
+ v_reserve(restype, 'down', node_output, unit, f+df_nReserves(node_output, restype, f, t), t)
+ v_reserve(restype, 'down', node_output, unit, f+df_reserves(node_output, restype, f, t), t)
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1295,7 +1298,7 @@ q_stateDownwardLimit(gn_state(grid, node), mft(m, f, t))${ sum(gn2gnu(grid, nod
+ sum(gn2gnu(grid_, node_input, grid, node, unit)${uft(unit, f, t)},
// Upward reserves from units that output energy to the node
+ sum(nuRescapable(restype, 'up', node_input, unit)${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length') },
+ v_reserve(restype, 'up', node_input, unit, f+df_nReserves(node_input, restype, f, t), t)
+ v_reserve(restype, 'up', node_input, unit, f+df_reserves(node_input, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1307,7 +1310,7 @@ q_stateDownwardLimit(gn_state(grid, node), mft(m, f, t))${ sum(gn2gnu(grid, nod
+ sum(gn2gnu(grid, node, grid_, node_output, unit)${uft(unit, f, t)},
// Upward reserves from units that use the node as energy input
+ sum(nuRescapable(restype, 'up', node_output, unit)${ ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length') },
+ v_reserve(restype, 'up', node_output, unit, f+df_nReserves(node_output, restype, f, t), t)
+ v_reserve(restype, 'up', node_output, unit, f+df_reserves(node_output, restype, f, t), t)
* sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1337,7 +1340,7 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), mft(m, f, t)) ..
and uft(unit, f, t)
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
},
+ v_reserve(restype, 'down', node_input, unit, f+df_nReserves(node_input, restype, f, t), t)
+ v_reserve(restype, 'down', node_input, unit, f+df_reserves(node_input, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1349,7 +1352,7 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), mft(m, f, t)) ..
and uft(unit, f, t)
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
},
+ v_reserve(restype, 'down', node_output, unit, f+df_nReserves(node_output, restype, f, t), t)
+ v_reserve(restype, 'down', node_output, unit, f+df_reserves(node_output, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1378,7 +1381,7 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), mft(m, f, t)) ..
and uft(unit, f, t)
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
},
+ v_reserve(restype, 'up', node_input, unit, f+df_nReserves(node_input, restype, f, t), t)
+ v_reserve(restype, 'up', node_input, unit, f+df_reserves(node_input, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......@@ -1390,7 +1393,7 @@ q_boundStateMaxDiff(gnn_boundState(grid, node, node_), mft(m, f, t)) ..
and uft(unit, f, t)
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
},
+ v_reserve(restype, 'up', node_output, unit, f+df_nReserves(node_output, restype, f, t), t)
+ v_reserve(restype, 'up', node_output, unit, f+df_reserves(node_output, restype, f, t), t)
/ sum(suft(effGroup, unit, f, t),
+ p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f, t)}
+ ts_effGroupUnit(effGroup, unit, 'slope', f, t) // Efficiency approximated using maximum slope of effGroup?
......
......@@ -145,7 +145,6 @@ Option clear = p_stepLength;
Option clear = msft;
Option clear = mft;
Option clear = ft;
Option clear = mft_nReserves;
// Initialize the set of active t:s and counters
Option clear = t_active;
......@@ -203,15 +202,6 @@ loop(cc(counter),
// Reduce the sample dimension
mft(mf(mSolve, f_solve), tt_interval(t)) = msft(mSolve, s, f_solve, t);
// The time periods that need to be locked when the gate closes t_solve + gate_closure --> t_solve + gate_closure + update_frequency (with consideration when t_jump is different than update_frequency)
mft_nReserves(node, restype, mf_realization(mSolve, f), tt_interval(t))
$ { p_nReserves(node, restype, 'update_frequency')
and p_nReserves(node, restype, 'gate_closure')
and ord(t) > tSolveFirst + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1, 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, 'update_frequency'))
}
= yes;
// Reduce the model dimension
ft(f_solve, tt_interval(t)) = mft(mSolve, f_solve, t);
......@@ -262,15 +252,6 @@ loop(cc(counter),
}
= yes;
// The time periods that need to be locked when the gate closes t_solve + gate_closure --> t_solve + gate_closure + update_frequency (with consideration when t_jump is different than update_frequency)
mft_nReserves(node, restype, mf_realization(mSolve, f), tt_interval(t))
$ { p_nReserves(node, restype, 'update_frequency')
and p_nReserves(node, restype, 'gate_closure')
and ord(t) > tSolveFirst + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1, 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, 'update_frequency'))
}
= yes;
// Reduce the sample dimension
mft(mf(mSolve, f_solve), tt_interval(t)) = msft(mSolve, s, f_solve, t);
......@@ -396,7 +377,7 @@ ft_realizedNoReset(ft_realized(f, t)) = yes;
msft_realizedNoReset(msft(mSolve, s, ft_realized(f, t))) = yes;
// Forecast index displacement between realized and forecasted intervals
// NOTE! This set cannot be reset without references to previously solved time steps in the stochastic tree becoming ill-defined!
// NOTE! This set cannot be reset without references to previously solved time steps in the stochastic tree becoming ill-defined!
df(f_solve(f), t_active(t))${ ord(t) <= tSolveFirst + mSettings(mSolve, 't_jump') }
= sum(mf_realization(mSolve, f_), ord(f_) - ord(f));
......@@ -408,12 +389,24 @@ df_central(ft(f,t))${ ord(t) = tSolveFirst + mSettings(mSolve, 't_forecastLeng
= sum(mf_central(mSolve, f_), ord(f_) - ord(f));
// Forecast index displacement between realized and forecasted intervals, required for locking reserves ahead of (dispatch) time.
Option clear = df_nReserves;
df_nReserves(node, restype, ft(f, t))${ p_nReserves(node, restype, 'update_frequency')
and p_nReserves(node, restype, 'gate_closure')
and ord(t) <= tSolveFirst + mSettings(mSolve, 't_jump') + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1 + mSettings(mSolve, 't_jump'), p_nReserves(node, restype, 'update_frequency'))
}
= sum(f_${ mf_realization(mSolve, f_) }, ord(f_) - ord(f));
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'))
}
= 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 mft-steps where the reserves are locked due to previous commitment
Option clear = ft_reservesFixed;
ft_reservesFixed(node, 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') - mod(tSolveFirst - 1 + mSettings(mSolve, 't_jump'), p_nReserves(node, restype, 'update_frequency')) - mSettings(mSolve, 't_jump')
}
= yes;
* =============================================================================
* --- Defining unit aggregations and ramps ------------------------------------
......
......@@ -174,8 +174,8 @@ v_gen.lo(gnuft(gnu_output(grid, node, unit), f, t))${ p_gnu(grid, node, unit,
= p_gnu(grid, node, unit, 'maxGen')
;
v_gen.up(gnuft(gnu_output(grid, node, unit), f, t))${ p_gnu(grid, node, unit, 'maxGen') < 0 }
= 0;
= 0
;
// Ramping capability of units not part of investment set
// NOTE: Apply the corresponding equations only to units with investment possibility,
// online variable, or reserve provision
......@@ -286,33 +286,65 @@ v_transferLeftward.up(gn2n_directional(grid, node, node_), ft(f, t))${ not p_gn
* --- Reserve Provision Boundaries --------------------------------------------
// 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', node, unit), f_solve(f+df_nReserves(node, restype, f, t)), t_active(t))${ nuft(node, unit, f, t)
and not (unit_investLP(unit) or unit_investMIP(unit))
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
}
= min ( p_nuReserves(node, unit, restype, 'up') * [ p_gnu('elec', node, unit, 'maxGen') + p_gnu('elec', node, unit, 'maxCons') ], // Generator + consuming unit res_range limit
v_gen.up('elec', node, unit, f, t) - v_gen.lo('elec', node, unit, f, t) // Generator + consuming unit available unit_elec. output delta
) // END min
* [
+ 1${mft_nReserves(node, restype, mSolve, f+df_nReserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
+ p_nuReserves(node, unit, restype, 'reserveContribution')${not mft_nReserves(node, restype, mSolve, f+df_nReserves(node, restype, f, t), t)}
] // END * min
;
v_reserve.up(nuRescapable(restype, 'down', node, unit), f_solve(f+df_nReserves(node, restype, f, t)), t_active(t))${ nuft(node, unit, f, t)
and not (unit_investLP(unit) or unit_investMIP(unit))
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
}
= min ( p_nuReserves(node, unit, restype, 'down') * [ p_gnu('elec', node, unit, 'maxGen') + p_gnu('elec', node, unit, 'maxCons') ], // Generator + consuming unit res_range limit
v_gen.up('elec', node, unit, f, t) - v_gen.lo('elec', node, unit, f, t) // Generator + consuming unit available unit_elec. output delta
) // END min
* [
+ 1${mft_nReserves(node, restype, mSolve, f+df_nReserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
+ p_nuReserves(node, unit, restype, 'reserveContribution')${not mft_nReserves(node, restype, mSolve, f+df_nReserves(node, restype, f, t), t)}
] // END * min
loop(f_solve(f), // Loop over the forecasts.
// 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', node, unit), f+df_reserves(node, restype, f, t), t_active(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 ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
}
= min ( p_nuReserves(node, unit, restype, 'up') * [ p_gnu('elec', node, unit, 'maxGen') + p_gnu('elec', node, unit, 'maxCons') ], // Generator + consuming unit res_range limit
v_gen.up('elec', node, unit, f, t) - v_gen.lo('elec', node, unit, f, t) // Generator + consuming unit available unit_elec. output delta
) // END min
* [
+ 1${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
+ p_nuReserves(node, unit, restype, 'reserveContribution')${not ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
] // END * min
;
v_reserve.up(nuRescapable(restype, 'down', node, unit), f+df_reserves(node, restype, f, t), t_active(t))
${ nuft(node, unit, f, t)
and not (unit_investLP(unit) or unit_investMIP(unit))
and ord(t) < tSolveFirst + p_nReserves(node, restype, 'reserve_length')
}
= min ( p_nuReserves(node, unit, restype, 'down') * [ p_gnu('elec', node, unit, 'maxGen') + p_gnu('elec', node, unit, 'maxCons') ], // Generator + consuming unit res_range limit
v_gen.up('elec', node, unit, f, t) - v_gen.lo('elec', node, unit, f, t) // Generator + consuming unit available unit_elec. output delta
) // END min
* [
+ 1${ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
+ p_nuReserves(node, unit, restype, 'reserveContribution')${not ft_reservesFixed(node, restype, f+df_reserves(node, restype, f, t), t)}
] // END * min
;
); // END loop(f_solve)
// Fix non-flow unit reserves at the gate closure of reserves
v_reserve.fx(nuRescapable(restype, up_down, node, unit), f_solve(f), t_active(t))
$ { ft_reservesFixed(node, restype, f, 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, t);
// Fix transfer of reserves at the gate closure of reserves
v_resTransferRightward.fx(restypeDirectionNode(restype, up_down, node), node_, f_solve(f), t_active(t))
$ { sum(grid, gn2n(grid, node, node_))
and ft_reservesFixed(node, restype, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and ft_reservesFixed(node_, restype, f, t)
}
= r_resTransferRightward(restype, up_down, node, node_, f, t);
v_resTransferLeftward.fx(restypeDirectionNode(restype, up_down, node), node_, f_solve(f), t_active(t))
$ { sum(grid, gn2n(grid, node, node_))
and ft_reservesFixed(node, restype, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and ft_reservesFixed(node_, restype, f, t)
}
= r_resTransferLeftward(restype, up_down, node, node_, f, t);
$ontext
// Fix slack variable for reserves that is used before the reserves need to be locked (vq_resMissing is used after this)
vq_resDemand.fx(restypeDirectionNode(restype, up_down, node), f_solve(f), t_active(t))
$ { ft_reservesFixed(node, restype, f, t) } // This set contains the combination of reserve types and time intervals that should be fixed
= r_qResDemand(restype, up_down, node, f, t);
$offtext
// Free reserves for the realization if needed
v_reserve.lo(nuRescapable(restype, up_down, node, unit), ft_realized(f,t))
......
......@@ -16,40 +16,8 @@ along with Backbone. If not, see <http://www.gnu.org/licenses/>.
$offtext
* =============================================================================
* --- Fixing some variable values after solve ---------------------------------
* --- Optional manipulation after solve ---------------------------------------
* =============================================================================
// Fix non-flow unit reserves at the gate closure of reserves
v_reserve.fx(nuRescapable(restype, up_down, node, unit), f_solve(f), t_active(t))
$ { mft_nReserves(node, restype, mSolve, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and ord(t) > mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
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)
}
= v_reserve.l(restype, up_down, node, unit, f, t);
// Fix transfer of reserves at the gate closure of reserves
v_resTransferRightward.fx(restypeDirectionNode(restype, up_down, node), node_, f_solve(f), t_active(t))
$ { sum(grid, gn2n(grid, node, node_))
and mft_nReserves(node, restype, mSolve, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and mft_nReserves(node_, restype, mSolve, f, t)
and ord(t) > mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
}
= v_resTransferRightward.l(restype, up_down, node, node_, f, t);
v_resTransferLeftward.fx(restypeDirectionNode(restype, up_down, node), node_, f_solve(f), t_active(t))
$ { sum(grid, gn2n(grid, node, node_))
and mft_nReserves(node, restype, mSolve, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and mft_nReserves(node_, restype, mSolve, f, t)
and ord(t) > mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
}
= v_resTransferLeftward.l(restype, up_down, node, node_, f, 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(restypeDirectionNode(restype, up_down, node), f_solve(f), t_active(t))
$ { mft_nReserves(node, restype, mSolve, f, t) // This set contains the combination of reserve types and time intervals that should be fixed
and ord(t) > mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
}
= vq_resDemand.l(restype, up_down, node, f, t);
$ontext
// Release some fixed values
......
......@@ -50,47 +50,26 @@ r_online(uft_online(unit, ft_realized(f, t)))$[ord(t) > mSettings(mSolve, 't_sta
+ v_online_MIP.l(unit, f, t)${ uft_onlineMIP(unit, f, t) }
;
// Reserve provisions of units
r_reserve(nuRescapable(restype, up_down, node, unit), f_solve(f), t_active(t))${ [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')] and
(
mft_nReserves(node, restype, mSolve, f, t) or
sum(f_, df_nReserves(node, restype, f_, t)) or
[ ord(t) > tSolveFirst + mSettings(mSolve, 't_jump')
and ord(t) <= tSolveFirst + mSettings(mSolve, 't_jump') + p_nReserves(node, restype, 'gate_closure') - mod(tSolveFirst - 1 + mSettings(mSolve, 't_jump'), p_nReserves(node, restype, 'update_frequency'))
and tSolveFirst <= mSettings(mSolve, 't_end') - mSettings(mSolve, 't_jump')
]
)
}
r_reserve(nuRescapable(restype, up_down, node, unit), f_solve(f), t_active(t))
${ ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')
and sum(f_, df_reserves(node, restype, f_, t))
}
= v_reserve.l(restype, up_down, node, unit, f, t)
;
// Reserve transfer capacity
r_resTransferRightward(restypeDirectionNode(restype, up_down, from_node), to_node, f_solve(f), t_active(t))${ [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')] and
(
restypeDirectionNode(restype, up_down, to_node)
and [ mft_nReserves(from_node, restype, mSolve, f, t)
or sum(f_, df_nReserves(from_node, restype, f_, t))
or [ ord(t) > tSolveFirst + mSettings(mSolve, 't_jump')
and ord(t) <= tSolveFirst + mSettings(mSolve, 't_jump') + p_nReserves(from_node, restype, 'gate_closure') - mod(tSolveFirst - 1 + mSettings(mSolve, 't_jump'), p_nReserves(from_node, restype, 'update_frequency'))
and tSolveFirst <= mSettings(mSolve, 't_end') - mSettings(mSolve, 't_jump')
]
]
)
}
= v_resTransferRightward.l(restype, up_down, from_node, to_node, f, t)
;
r_resTransferLeftward(restypeDirectionNode(restype, up_down, from_node), to_node, f_solve(f), t_active(t))${ [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')] and
(
{ restypeDirectionNode(restype, up_down, to_node)
and [ mft_nReserves(from_node, restype, mSolve, f, t)
or sum(f_, df_nReserves(from_node, restype, f_, t))
or [ ord(t) > tSolveFirst + mSettings(mSolve, 't_jump')
and ord(t) <= tSolveFirst + mSettings(mSolve, 't_jump') + p_nReserves(from_node, restype, 'gate_closure') - mod(tSolveFirst - 1 + mSettings(mSolve, 't_jump'), p_nReserves(from_node, restype, 'update_frequency'))
and tSolveFirst <= mSettings(mSolve, 't_end') - mSettings(mSolve, 't_jump')
]
]
}
)
}
= v_resTransferLeftward.l(restype, up_down, from_node, to_node, f, t)
r_resTransferRightward(restypeDirectionNode(restype, up_down, from_node), to_node, f_solve(f+df_reserves(from_node, restype, f, t)), t_active(t))
${ [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')]
and restypeDirectionNode(restype, up_down, to_node)
and df_reserves(to_node, restype, f, t)
}
= v_resTransferRightward.l(restype, up_down, from_node, to_node, f+df_reserves(from_node, restype, f, t), t)
;
r_resTransferLeftward(restypeDirectionNode(restype, up_down, from_node), to_node, f_solve(f+df_reserves(from_node, restype, f, t)), t_active(t))
${ [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')]
and restypeDirectionNode(restype, up_down, to_node)
and df_reserves(to_node, restype, f, t)
}
= v_resTransferLeftward.l(restype, up_down, from_node, to_node, f+df_reserves(from_node, restype, f, t), t)
;
// Unit startup and shutdown history
r_startup(unit, starttype, ft_realized(f, t))${ uft_online(unit, f, t) and [ord(t) > mSettings(mSolve, 't_start') + mSettings(mSolve, 't_initializationPeriod')] }
......
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