p_effGroupUnit(effDirectOn, unit, 'section') = p_unit(unit, 'section');
p_effGroupUnit(effDirectOn, unit, 'section')$(not p_effGroupUnit(effDirectOn, unit, 'section')) = // Unless section has been defined, it is calculated based on the opFirstCross
p_stepLength(mf(mSolve, fSolve), t)$tInterval(t) = mSettings(mSolve, 'intervalInHours'); // p_stepLength will hold the length of the interval in hours in model equations
elseif mInterval(mSolve, 'intervalLength', counter) > 1, // intervalLength > 1 (not defined if intervalLength < 1)
loop(t$[ord(t) >= tSolveFirst + tCounter and ord(t) < min(tSolveFirst + mInterval(mSolve, 'intervalEnd', counter), tSolveLast)], // Loop t relevant for each interval (interval borders defined by intervalEnd) - but do not go beyond tSolveLast
if (not mod(tCounter - mInterval(mSolve, 'intervalEnd', counter), mInterval(mSolve, 'intervalLength', counter)), // Skip those t's that are not at the start of any interval
...
...
@@ -45,14 +46,6 @@ $offOrder
// Set the previous time step displacement
pt(t+intervalLength) = -intervalLength;
);
if (mInterval(mSolve, 'intervalEnd', counter) <= mSettings(mSolve, 't_forecastLength'),
//fft_dynamic(f,f_,t)$(ord(f) = ord(f_) and ft(f,t) and [ord(t) < tSolveFirst + mSettings(mSolve, 't_forecastLength') + 1 or ord(t) > tSolveFirst + mSettings(mSolve, 't_forecastLength') + 1]) = yes;
//fft_dynamic(f,f_,t)$(ord(f_) > 1 and mf(mSolve, f_) and ft(f,t) and ord(t) = tSolveFirst + mSettings(mSolve, 't_forecastLength') + 1) = yes;
//fft_dynamic(f,f_,tSolve+tDispatchCurrent) = no;
continueLoop = 1;
cf(f,t) = no;
loop(t$(ord(t) > tSolveFirst + mSettings(mSolve,'t_forecastLength') and continueLoop), // Loop through all the different intervals set in the model definition file
ft(f, t) "Combination of forecasts and time periods in the current model"
ft_dynamic(f, t) "ft without first t and with tLast+1 (moved right)"
//fft_dynamic(f, f, t) "ft without first t and with tLast+1 (moved right) and additional forecasts when decreasing the number of scenarios in the forecast tree"
ft_full(f, t) "ft with all t's in the solve including tSolve and tLast+1"
ft_realized(f, t) "Realized ft"
ft_realizedLast(f, t) "Last realized ft"
ft_limits(f, t) "All ft for which variable limits are set within the tSolve loop"
ft_new(f, t) "Newly introduced f,t to be used in calculating parameter/variable values"
mft(mType, f, t) "Combination of forecasts and time periods in the models"
mft_(mType, f, t) "Combination of forecasts and time periods in the models"
...
...
@@ -90,10 +92,13 @@ Sets
mftLastSteps(mType, f, t) "Last time periods of the model (can be end of forecasts or end of samples)"
modelSolves(mType, t) "when different models are to be solved"
fSolve(f) "forecasts in the model to be solved next"
tSolveDispatch(t)
* --- Sets used for the changing unit aggregation and efficiency approximations
uft(unit, f, t) "Enables aggregation of units for later time periods"
uft_online(unit, f, t) "Units with online and startup variables on time periods"
uft_limits(unit, f, t) "Used to set limits in the tSolve loop"
uft_limits_online(unit, f, t) "Used to set limits in the tSolve loop"
nuft(node, unit, f, t) "Enables aggregation of nodes and units for later time periods"
gnuft(grid, node, unit, f, t) "Enables aggregation of nodes and units for later time periods"
gnuft_ramp(grid, node, unit, f, t) "Units with ramp requirements or costs"
tSolveFirst "counter (ord) for the first t in the solve"
tSolveLast "counter for the last t in the solve"
tDispatchCurrent "counter for the current t in the dispatch loop" /0/
tCounter "counter for t" /0/
lastCounter "last member in use of the general counter"
ts_length "Length of time series (t)"
...
...
@@ -53,12 +54,14 @@ Scalar p_sWeightSum "Sum of sample weights";
Parameters
pt(t) "Displacement needed to reach the previous time period (in time periods)"
pf(f, t) "Displacement needed to reach the previous forecast (in forecasts)"
cf(f, t) "Displacement needed to reach the current forecast (in forecasts) - this is needed when the forecast tree gets reduced in dynamic equations"
ct(t) "Circular t displacement if the time series data is not long enough to cover the model horizon"
t_bind(t) "Displacement to reach the binding time period in the parent sample (in time periods). Can skip with aggregated steps as well as when connecting samples."
ft_bind(f, t) "Displacement to reach the binding forecast (in forecasts) in the current model"
mt_bind(mType, t) "Displacement to reach the binding time period in the parent sample (in time periods) in the models"
mft_bind(mType, f, t) "Displacement to reach the binding forecast (in forecasts) in the models"
p_slackDirection(slack) "+1 for upward slacks and -1 for downward slacks"
tForecastNext(mType) "When the next forecast will be availalbe (ord time)"
;
* --- Stochastic data parameters ----------------------------------------------
q_resTransfer(grid, node, node, f, t) "Transfer of energy and capacity reservations are less than the transfer capacity"
q_maxDownward(grid, node, unit, f, t) "Downward commitments will not undercut power plant minimum load constraints or maximum elec. consumption"
q_maxUpward(grid, node, unit, f, t) "Upward commitments will not exceed maximum available capacity or consumed power"
q_bindOnline(unit, mType, f, t) "Couple online variable when joining forecasts or when joining sample time periods"
q_startup(unit, f, t) "Capacity started up is greater than the difference of online cap. now and in the previous time step"
q_genRamp(grid, node, mType, unit, f, t) "Record the ramps of units with ramp restricitions or costs"
q_genRampChange(grid, node, mType, unit, f, t) "Record the ramp rates of units with ramping costs"
...
...
@@ -139,7 +138,7 @@ q_obj ..
q_balance(gn(grid, node), m, ft_dynamic(f, t))${p_stepLength(m, f+pf(f,t), t+pt(t)) and not p_gn(grid, node, 'boundAll')} .. // Energy/power balance dynamics solved using implicit Euler discretization
// The left side of the equation is the change in the state (will be zero if the node doesn't have a state)
+ p_gn(grid, node, 'energyStoredPerUnitOfState')$gn_state(grid, node) // Unit conversion between v_state of a particular node and energy variables (defaults to 1, but can have node based values if e.g. v_state is in Kelvins and each node has a different heat storage capacity)
* ( + v_state(grid, node, f, t) // The difference between current
* ( + v_state(grid, node, f+cf(f,t), t) // The difference between current
- v_state(grid, node, f+pf(f,t), t+pt(t)) // ... and previous state of the node
+ v_state(grid, from_node, f, t) // Incoming diffusion based on the state of the neighbouring node
+ v_state(grid, from_node, f+cf(f,t), t) // Incoming diffusion based on the state of the neighbouring node
$$ifi '%rampSched%' == 'yes' + v_state(grid, from_node, f+pf(f,t), t+pt(t)) // Ramp schedule averaging, NOTE! State and other terms use different indeces for non-ramp-schedule!
$$ifi '%rampSched%' == 'yes' + v_transfer(grid, from_node, node, f, t) // Ramp schedule averaging, NOTE! State and other terms use different indeces for non-ramp-schedule!
$$ifi '%rampSched%' == 'yes' + v_transfer(grid, from_node, node, f+cf(f,t), t) // Ramp schedule averaging, NOTE! State and other terms use different indeces for non-ramp-schedule!
)
)
// Controlled energy transfer to other nodes from this one
- sum(to_node$(gn2n(grid, node, to_node)),
+ v_transfer(grid, node, to_node, f+pf(f,t), t+pt(t)) // Transfer losses accounted for in the previous term
// Dummy generation variables, for feasibility purposes
+ vq_gen('increase', grid, node, f+pf(f,t), t+pt(t)) // Note! When stateSlack is permitted, have to take caution with the penalties so that it will be used first
- vq_gen('decrease', grid, node, f+pf(f,t), t+pt(t)) // Note! When stateSlack is permitted, have to take caution with the penalties so that it will be used first
q_genRampChange(gn(grid, node), m, unit, ft_dynamic(f, t))${ gnuft_ramp(grid, node, unit, f, t) AND [ p_gnu(grid, node, unit, 'rampUpCost') OR p_gnu(grid, node, unit, 'rampDownCost') ]} ..
+ v_genRampChange(grid, node, unit, 'up', f, t)
- v_genRampChange(grid, node, unit, 'down', f, t)
+ v_genRampChange(grid, node, unit, 'up', f+cf(f,t), t)
- v_genRampChange(grid, node, unit, 'down', f+cf(f,t), t)