Commit 5c5c68b2 authored by Topi Rasku's avatar Topi Rasku
Browse files

Major rewriting and cleanup of the efficiency approximations (directOff,...

Major rewriting and cleanup of the efficiency approximations (directOff, directOn, lambda), and some of the related sets.

Previously, the efficiency approximations required specific efficiency data, and no more than 2 points of data were used. Now the approximations use all the relevant input data they can find (up to the defined maximum indeces) when determining the parameters, and tests have been implemented in "1e_inputs.gms" to abort Backbone execution and warn the user of incompatible input data with the desired approximations.

However, without minimum load, there are still issues with the "directOn" and "lambda" approximations that could perhaps be solved.
parent b0445997
......@@ -58,108 +58,113 @@ loop(m,
);
* Parse through effLevelSelectorUnit and convert selected effSelectors into sets representing those selections
* Parse through effLevelGroupUnit and convert selected effSelectors into sets representing those selections
loop(unit,
cum_lambda = 0;
cum_slope = 0;
loop(effLevel$sum(m, mSettingsEff(m, effLevel)),
// effSelector using DirectOn
if (sum(effSelector$effDirectOn(effSelector), effLevelGroupUnit(effLevel, effSelector, unit)),
loop(effSelector$effDirectOn(effSelector),
if(effLevelGroupUnit(effLevel, effSelector, unit),
effGroup(effSelector) = yes;
effGroupSelector(effSelector, effSelector) = yes;
effGroupSelectorUnit(effSelector, unit, effSelector) = yes;
effLevelSelectorUnit(effLevel, effSelector, unit) = yes;
);
// effSelector using DirectOff
if (sum(effDirectOff, effLevelGroupUnit(effLevel, effDirectOff, unit)),
loop(effDirectOff${effLevelGroupUnit(effLevel, effDirectOff, unit)},
effGroup(effDirectOff) = yes;
effGroupSelector(effDirectOff, effDirectOff) = yes;
effGroupSelectorUnit(effDirectOff, unit, effDirectOff) = yes;
);
);
// effSelector using DirectOff
if (sum(effSelector$effDirectOff(effSelector), effLevelGroupUnit(effLevel, effSelector, unit)),
loop(effSelector$effDirectOff(effSelector),
if(effLevelGroupUnit(effLevel, effSelector, unit),
effGroup(effSelector) = yes;
effGroupSelector(effSelector, effSelector) = yes;
effGroupSelectorUnit(effSelector, unit, effSelector) = yes;
effLevelSelectorUnit(effLevel, effSelector, unit) = yes;
);
// effSelector using DirectOn
if (sum(effDirectOn, effLevelGroupUnit(effLevel, effDirectOn, unit)),
loop(effDirectOn${effLevelGroupUnit(effLevel, effDirectOn, unit)},
effGroup(effDirectOn) = yes;
effGroupSelector(effDirectOn, effDirectOn) = yes;
effGroupSelectorUnit(effDirectOn, unit, effDirectOn) = yes;
);
);
// effSelectors using Lambda
if (sum(effSelector_$effLambda(effSelector_), effLevelGroupUnit(effLevel, effSelector_, unit)),
count = 0;
loop(effSelector$effLambda(effSelector),
count = count + 1;
if(effLevelGroupUnit(effLevel, effSelector, unit),
count_lambda = count;
cum_lambda = cum_lambda + count_lambda;
effGroup(effSelector) = yes;
);
);
count = 0;
loop(effSelector$effLambda(effSelector),
count = count + 1;
if( count > cum_lambda - count_lambda and count <= cum_lambda,
effLevelSelectorUnit(effLevel, effSelector, unit) = yes;
effGroupSelectorUnit(effGroup, unit, effSelector)$effLevelGroupUnit(effLevel, effGroup, unit) = yes;
);
if ( count = count_lambda,
count_lambda2 = 0;
loop(effSelector_$effLambda(effSelector_),
count_lambda2 = count_lambda2 + 1;
if (count_lambda2 > cum_lambda - count_lambda and count_lambda2 <= cum_lambda,
effGroupSelector(effSelector, effSelector_) = yes;
);
if (sum(effLambda, effLevelGroupUnit(effLevel, effLambda, unit)),
loop(effLambda${effLevelGroupUnit(effLevel, effLambda, unit)},
effGroup(effLambda) = yes;
loop(effLambda_${ord(effLambda_) <= ord(effLambda)},
effGroupSelector(effLambda, effLambda_) = yes;
effGroupSelectorUnit(effLambda, unit, effLambda_) = yes;
);
);
);
);
// Calculate parameters for units using direct input output conversion with online variable
loop(effSelector$sum(effDirectOn$effGroupSelector(effDirectOn, effSelector), 1),
p_effUnit(effSelector, unit, 'lb') = p_unit(unit, 'rb00');
p_effUnit(effSelector, unit, 'rb') = p_unit(unit, 'rb01');
p_effUnit(effSelector, unit, 'section')$(not p_unit(unit, 'eff01')) = 0;
p_effUnit(effSelector, unit, 'slope')$(not p_unit(unit, 'eff01')) = 1 / p_unit(unit, 'eff00');
p_effUnit(effSelector, unit, 'section')$p_unit(unit, 'eff01') =
+ p_unit(unit, 'rb01') / p_unit(unit, 'eff01')
- [p_unit(unit, 'rb01') - 0]
/ [p_unit(unit, 'rb01') - p_unit(unit, 'rb00')]
* [1 / p_unit(unit, 'eff01') * p_unit(unit, 'rb01') - 1 / p_unit(unit, 'eff00') * p_unit(unit, 'rb00')];
p_effUnit(effSelector, unit, 'slope')$p_unit(unit, 'eff01') =
+ 1 / p_unit(unit, 'eff01') - p_effUnit(effSelector, unit, 'section');
);
// Calculate parameters for units using direct input output conversion without online variable
loop(effSelector$sum(effDirectOff$effGroupSelector(effDirectOff, effSelector), 1),
p_effUnit(effSelector, unit, 'rb') = 1;
p_effUnit(effSelector, unit, 'lb') = 0;
p_effUnit(effSelector, unit, 'section')$(not p_unit(unit, 'eff01')) = 0;
p_effUnit(effSelector, unit, 'slope')$(not p_unit(unit, 'eff01')) = 1 / p_unit(unit, 'eff00');
p_effUnit(effSelector, unit, 'section')$p_unit(unit, 'eff01') = 0;
p_effUnit(effSelector, unit, 'slope')$p_unit(unit, 'eff01') = 1 / p_unit(unit, 'eff01');
loop(effGroupSelectorUnit(effDirectOff, unit, effDirectOff_),
p_effUnit(effDirectOff, unit, effDirectOff_, 'lb') = 0; // No min load for the DirectOff approximation
p_effUnit(effDirectOff, unit, effDirectOff_, 'rb') = 1; // No max load for the DirectOff approximation
p_effUnit(effDirectOff, unit, effDirectOff_, 'slope') = 1 / smax(eff, p_unit(unit, eff)); // Uses maximum found efficiency.
p_effUnit(effDirectOff, unit, effDirectOff_, 'section') = 0; // No section for the DirectOff approximation
);
// Calculate parameters for units using direct input output conversion with online variable
loop(effGroupSelectorUnit(effDirectOn, unit, effDirectOn_),
p_effUnit(effDirectOn, unit, effDirectOn_, 'lb') = p_unit(unit, 'rb00'); // rb00 contains the possible min load of the unit
p_effUnit(effDirectOn, unit, effDirectOn_, 'rb') = smax(rb, p_unit(unit, rb)); // Maximum load determined by the largest 'rb' parameter found in data
loop(rb__${p_unit(unit, rb__) = smax(rb, p_unit(unit, rb))}, // Find the maximum defined 'rb'.
loop(eff__${ord(eff__) = ord(rb__)}, // ... and the corresponding 'eff'.
loop(rb_${p_unit(unit, rb_) = smin(rb${p_unit(unit, rb)}, p_unit(unit, rb))}, // Find the minimum defined nonzero 'rb'.
loop(eff_${ord(eff_) = ord(rb_)}, // ... and the corresponding 'eff'.
// Calculating the slope based on the first nonzero and the last defined data points.
p_effUnit(effDirectOn, unit, effDirectOn_, 'slope') =
+ (p_unit(unit, rb__) / p_unit(unit, eff__) - p_unit(unit, rb_) / p_unit(unit, eff_))
/ (p_unit(unit, rb__) - p_unit(unit, rb_));
// Calculating the section based on the slope and the last defined point.
p_effUnit(effDirectOn, unit, effDirectOn_, 'section') =
( 1 / p_unit(unit, eff__) - p_effUnit(effDirectOn, unit, effDirectOn_, 'slope') )
* p_unit(unit, rb__);
);
);
);
);
);
// Calculate lambdas
count_lambda2 = 0;
loop(effSelector$(effLambda(effSelector) and effLevelSelectorUnit(effLevel, effSelector, unit)),
p_effUnit(effSelector, unit, 'rb') = ((count_lambda-1 - count_lambda2) * p_unit(unit, 'rb00') + count_lambda2 * p_unit(unit, 'rb01')) / (count_lambda - 1);
p_effUnit(effSelector, unit, 'lb') = p_effUnit(effSelector, unit, 'rb'); // Required for p_effGroupUnit and q_maxDownward
p_effUnit(effSelector, unit, 'slope')$effLevelSelectorUnit(effLevel, effSelector, unit)
= ((count_lambda-1 - count_lambda2) * (1 / p_unit(unit, 'eff00')) + count_lambda2 * (1 / p_unit(unit, 'eff01'))) / (count_lambda - 1);
count_lambda2 = count_lambda2 + 1;
loop(effGroupSelectorUnit(effLambda, unit, effLambda_),
p_effUnit(effLambda, unit, effLambda_, 'lb') = p_unit(unit, 'rb00'); // 'rb00' contains the possible minload of the unit, recorded for every lambda for p_effGroupUnit.
// For the first lambda, simply use the first data point
if(ord(effLambda_) = 1,
p_effUnit(effLambda, unit, effLambda_, 'rb') = p_unit(unit, 'rb00'); // 'rb00' also works as the lowest lambda point.
p_effUnit(effLambda, unit, effLambda_, 'slope') = 1 / p_unit(unit, 'eff00'); // eff00 works as the lowest lambda slope.
// For the last lambda, use the last data point
elseif ord(effLambda_) = ord(effLambda),
loop(rb__${p_unit(unit, rb__) = smax(rb, p_unit(unit, rb))}, // Find the maximum defined 'rb'.
loop(eff__${ord(eff__) = ord(rb__)}, // ... and the corresponding 'eff'.
p_effUnit(effLambda, unit, effLambda_, 'rb') = p_unit(unit, rb__); // Last defined 'rb'.
p_effUnit(effLambda, unit, effLambda_, 'slope') = 1 / p_unit(unit, eff__); // Last defined 'eff'.
);
);
// For the intermediary lambdas, use averages of the data points on each side.
else
count = sum(rb${p_unit(unit, rb)}, 1) + 1${not p_unit(unit, 'rb00')}; // Count the data points to correctly establish the lambda intervals, have to separately account for the possibility of 'rb00' = 0.
tmp = (ord(effLambda_) - 1) / (ord(effLambda) - 1 ) * count; // Determines the lambda interval.
count_lambda = floor(tmp); // Determine the data point index before the lambda
count_lambda2 = ceil(tmp); // Determine the data point index after the lambda
loop(rb__${ord(rb__) = count_lambda2}, // Find the ceiling data point 'rb'.
loop(eff__${ord(eff__) = count_lambda2}, // ... and the corresponding 'eff'.
loop(rb_${ord(rb_) = count_lambda}, // Find the floor data point 'rb'.
loop(eff_${ord(eff_) = count_lambda}, // .. and the corresponding 'eff'.
p_effUnit(effLambda, unit, effLambda_, 'rb') = (tmp - count_lambda) * p_unit(unit, rb__) + (count_lambda2 - tmp) * p_unit(unit, rb_); // Average the 'rb' between the found data points, weighted by tmp.
p_effUnit(effLambda, unit, effLambda_, 'slope') = (tmp - count_lambda) / p_unit(unit, eff__) + (count_lambda2 - tmp) / p_unit(unit, eff_); // Average the 'eff' between the found data points, weighed by tmp.
);
);
);
);
);
);
);
);
); // END LOOP OVER effLevel
); // END LOOP OVER unit
// Calculate unit wide parameters for each efficiency group
loop(unit,
loop(effLevel$sum(m, mSettingsEff(m, effLevel)),
loop(effLevelGroupUnit(effLevel, effGroup, unit),
p_effGroupUnit(effGroup, unit, 'rb') = smax(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), p_effUnit(effSelector, unit, 'rb'));
p_effGroupUnit(effGroup, unit, 'lb') = smin(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), p_effUnit(effSelector, unit, 'lb'));
p_effGroupUnit(effGroup, unit, 'rb') = smax(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), p_effUnit(effGroup, unit, effSelector, 'rb'));
p_effGroupUnit(effGroup, unit, 'lb') = smin(effSelector${effGroupSelectorUnit(effGroup, unit, effSelector)}, p_effUnit(effGroup, unit, effSelector, 'lb'));
p_effGroupUnit(effGroup, unit, 'slope') = smax(effSelector${effGroupSelectorUnit(effGroup, unit, effSelector)}, p_effUnit(effGroup, unit, effSelector, 'slope')); // NOTE! This is a worst case scenario.
);
);
);
......
......@@ -130,6 +130,7 @@ $onOrder
gnuft(grid, node, unit, f, t) = no;
gnuft(grid, node, unit, f, t)$(gn(grid, node) and nuft(node, unit, f, t)) = yes;
// Defining unit efficiency groups etc.
suft(effGroup, unit, f, t) = no;
loop(effLevel$mSettingsEff(mSolve, effLevel),
tInterval(t) = no;
......@@ -143,70 +144,93 @@ $onOrder
);
sufts(effGroup, unit, f, t, effSelector) = no;
sufts(effGroup, unit, f, t, effSelector)$(effGroupSelector(effGroup, effSelector) and suft(effGroup, unit, f, t)) = yes;
uft_online(unit, ft(f, t))${unit_online(unit)} = yes;
uft_online(unit, ft(f, t))$suft('directOff', unit, f, t) = no;
uft_online(unit, ft(f, t)) = no; // Initialize the 'uft_online' set so that no units use online variables by default.
loop(suft(effOnline, unit, ft(f, t)), // Determine the time steps when units need to have online variables.
uft_online(unit, f, t) = yes;
);
pf(ft(f,t))$(ord(t) eq ord(tSolve) + 1) = 1 - ord(f);
// Calculate time series for unit parameters when necessary and/or possible
loop(unit${p_unit(unit, 'useTimeseries')},
// cum_lambda = 0;
// cum_slope = 0;
loop(effLevel${mSettingsEff(mSolve, effLevel)},
// Calculate time series for unit parameters using direct input output conversion with online variable
loop(effSelector$sum(effDirectOn$effGroupSelector(effDirectOn, effSelector), 1),
ts_effUnit(effSelector, unit, 'lb', ft(f, t))$ts_unit_(unit, 'rb00', f, t) = ts_unit_(unit, 'rb00', f, t);
ts_effUnit(effSelector, unit, 'rb', ft(f, t))$ts_unit_(unit, 'rb01', f, t) = ts_unit_(unit, 'rb01', f, t);
// ts_effUnit(effSelector, unit, 'section', ft(f, t))$(not ts_unit(unit, 'eff01')) = 0;
// ts_effUnit(effSelector, unit, 'slope')$(not p_unit(unit, 'eff01')) = 1 / p_unit(unit, 'eff00');
ts_effUnit(effSelector, unit, 'section', ft(f, t))$ts_unit_(unit, 'eff01', f, t) =
+ 1 / ts_unit_(unit, 'eff01', f, t)
- [p_unit(unit, 'rb01')${not ts_unit_(unit, 'rb01', f, t)} + ts_unit_(unit, 'rb01', f, t) - 0]
/ [p_unit(unit, 'rb01')${not ts_unit_(unit, 'rb01', f, t)} + ts_unit_(unit, 'rb01', f, t)
- p_unit(unit, 'rb00')${not ts_unit_(unit, 'rb00', f, t)} - ts_unit_(unit, 'rb00', f, t)]
* [(p_unit(unit, 'rb01')${not ts_unit_(unit, 'rb01', f, t)} + ts_unit_(unit, 'rb01', f, t))
/ (p_unit(unit, 'eff01')${not ts_unit_(unit, 'eff01', f, t)} + ts_unit_(unit, 'eff01', f, t))
- (p_unit(unit, 'rb00')${not ts_unit_(unit, 'rb00', f, t)} + ts_unit_(unit, 'rb00', f, t))
/ (p_unit(unit, 'eff00')${not ts_unit_(unit, 'eff00', f, t)} + ts_unit_(unit, 'eff00', f, t))];
ts_effUnit(effSelector, unit, 'slope', ft(f, t))$ts_unit_(unit, 'eff01', f, t) =
+ 1 / ts_unit_(unit, 'eff01', f, t) - ts_effUnit(effSelector, unit, 'section', f, t);
);
// Calculate time series for unit parameters using direct input output conversion without online variable
loop(effSelector$sum(effDirectOff$effGroupSelector(effDirectOff, effSelector), 1),
// p_effUnit(effSelector, unit, 'rb') = 1;
// p_effUnit(effSelector, unit, 'lb') = 0;
// p_effUnit(effSelector, unit, 'section')$(not p_unit(unit, 'eff01')) = 0;
ts_effUnit(effSelector, unit, 'slope', ft(f, t))${ts_unit_(unit, 'eff00', f, t)} = 1 / ts_unit_(unit, 'eff00', f, t);
// p_effUnit(effSelector, unit, 'section')${p_unit(unit, 'eff01')} = 0;
ts_effUnit(effSelector, unit, 'slope', ft(f, t))${ts_unit_(unit, 'eff01', f, t)} = 1 / ts_unit_(unit, 'eff01', f, t);
// Calculate time series form parameters for units using direct input output conversion without online variable
// Always constant 'lb', 'rb', and 'section', so need only to define 'slope'.
loop(effGroupSelectorUnit(effDirectOff, unit, effDirectOff_),
ts_effUnit(effDirectOff, unit, effDirectOff_, 'slope', ft(f, t))${sum(eff, ts_unit(unit, eff, f, t))} = // NOTE!!! Averages the slope over all available data.
sum(eff${ts_unit(unit, eff, f, t)}, 1 / ts_unit(unit, eff, f, t)) / sum(eff${ts_unit(unit, eff, f, t)}, 1);
);
// Calculate lambdas
count_lambda2 = 0;
loop(effSelector$(effLambda(effSelector) and effLevelSelectorUnit(effLevel, effSelector, unit)),
ts_effUnit(effSelector, unit, 'rb', ft(f, t))${ts_unit_(unit, 'rb00', f, t) OR ts_unit_(unit, 'rb01', f, t)} =
+ ((count_lambda-1 - count_lambda2) * (p_unit(unit, 'rb00')${not ts_unit_(unit, 'rb00', f, t)} + ts_unit_(unit, 'rb00', f, t))
+ count_lambda2 * (p_unit(unit, 'rb01')${not ts_unit_(unit, 'rb01', f ,t)} + ts_unit_(unit, 'rb01', f, t)))
/ (count_lambda - 1);
//no lb for lambdas, since number of borders same as number of slopes p_effUnit(effSelector, unit, 'lb') = ((count_lambda-1 - count_lambda2 + 1) * p_unit(unit, 'rb00') + (count_lambda2 - 1) * p_unit(unit, 'rb01')) / (count_lambda - 1);
ts_effUnit(effSelector, unit, 'slope', ft(f, t))${effLevelSelectorUnit(effLevel, effSelector, unit) AND (ts_unit_(unit, 'eff00', f, t) OR ts_unit_(unit, 'eff01', f, t))} =
+ ((count_lambda-1 - count_lambda2) * (1 / (p_unit(unit, 'eff00')${not ts_unit_(unit, 'eff00', f, t)} + ts_unit_(unit, 'eff00', f, t)))
+ count_lambda2 * (1 / (p_unit(unit, 'eff01')${not ts_unit_(unit, 'eff01', f, t)} + ts_unit_(unit, 'eff01', f, t))))
/ (count_lambda - 1);
count_lambda2 = count_lambda2 + 1;
);
// NOTE! Using the same methodology for the directOn and lambda approximations in time series form might require looping over ft(f,t) to find the min and max 'eff' and 'rb'
// Alternatively, one might require that the 'rb' is defined in a similar structure, so that the max 'rb' is located in the same index for all ft(f,t)
// Calculate time series form parameters for units using direct input output conversion with online variable
* loop(effGroupSelectorUnit(effDirectOn, unit, effDirectOn_),
* ts_effUnit(effDirectOn, unit, effDirectOn_, 'lb', ft(f, t))${ts_unit(unit, 'rb00', f, t)} = ts_unit(unit, 'rb00', f, t); // rb00 contains the possible min load of the unit
* ts_effUnit(effDirectOn, unit, effDirectOn_, 'rb', ft(f, t))${sum(rb, ts_unit(unit, rb, f, t))} = smax(rb, ts_unit(unit, rb, f, t)); // Maximum load determined by the largest 'rb' parameter found in data
* loop(rb__${ts_unit(unit, rb__, ft(f, t)) = smax(rb, ts_unit(unit, rb, f, t))}, // Find the maximum defined 'rb'.
* loop(eff__${ord(eff__) = ord(rb__)}, // ... and the corresponding 'eff'.
* loop(rb_${ts_unit(unit, rb_, ft(f, t)) = smin(rb, ts_unit(unit, rb, f, t))}, // Find the minimum defined nonzero 'rb'.
* loop(eff_${ord(eff_) = ord(rb_)}, // ... and the corresponding 'eff'.
* // Calculating the slope based on the first nonzero and the last defined data points.
* ts_effUnit(effDirectOn, unit, effDirectOn_, 'slope', ft(f, t)) =
* + (ts_unit(unit, rb__, f, t) / ts_unit(unit, eff__, f, t) - ts_unit(unit, rb_, f, t) / ts_unit(unit, eff_, f, t))
* / (ts_unit(unit, rb__, f, t) - ts_unit(unit, rb_, f, t));
* // Calculating the section based on the slope and the last defined point.
* ts_effUnit(effDirectOn, unit, effDirectOn_, 'section', ft(f, t)) =
* ( 1 / ts_unit(unit, eff__, f, t) - ts_effUnit(effDirectOn, unit, effDirectOn_, 'slope', f, t) )
* * ts_unit(unit, rb__, f, t);
* );
* );
* );
* );
* );
);
);
// Calculate lambdas
* loop(effGroupSelectorUnit(effLambda, unit, effLambda_),
* ts_effUnit(effLambda, unit, effLambda_, 'lb', ft(f, t)) = ts_unit(unit, 'rb00', f, t); // 'rb00' contains the possible minload of the unit, recorded for every lambda for ts_effGroupUnit.
* // For the first lambda, simply use the first data point
* if(ord(effLambda_) = 1,
* ts_effUnit(effLambda, unit, effLambda_, 'rb', ft(f, t)) = ts_unit(unit, 'rb00', f, t); // 'rb00' also works as the lowest lambda point.
* ts_effUnit(effLambda, unit, effLambda_, 'slope', ft(f, t)) = 1 / ts_unit(unit, 'eff00', f, t); // eff00 works as the lowest lambda slope.
* // For the last lambda, use the last data point
* elseif ord(effLambda_) = ord(effLambda),
* loop(rb__${ts_unit(unit, rb__, ft(f, t)) = smax(rb, ts_unit(unit, rb, f, t))}, // Find the maximum defined 'rb'.
* loop(eff__${ord(eff__) = ord(rb__)}, // ... and the corresponding 'eff'.
* ts_effUnit(effLambda, unit, effLambda_, 'rb', ft(f, t)) = ts_unit(unit, rb__, f, t); // Last defined 'rb'.
* ts_effUnit(effLambda, unit, effLambda_, 'slope', ft(f, t)) = 1 / ts_unit(unit, eff__, f, t); // Last defined 'eff'.
* );
* );
* // For the intermediary lambdas, use averages of the data points on each side.
* else
* count = sum(rb${ts_unit(unit, rb, ft(f, t))}, 1) + 1${not ts_unit(unit, 'rb00', f, t)}; // Count the data points to correctly establish the lambda intervals, have to separately account for the possibility of 'rb00' = 0.
* count_lambda = floor( (ord(effLambda_) - 1) / (ord(effLambda) - 1) * count ); // Determine the data point index before the lambda
* count_lambda2 = ceil( (ord(effLambda_) - 1) / (ord(effLambda) - 1) * count ); // Determine the data point index after the lambda
* loop(rb__${ord(rb__) = count_lambda2}, // Find the ceiling data point 'rb'.
* loop(eff__${ord(eff__) = count_lambda2}, // ... and the corresponding 'eff'.
* loop(rb_${ord(rb_) = count_lambda}, // Find the floor data point 'rb'.
* loop(eff_${ord(eff_) = count_lambda}, // .. and the corresponding 'eff'.
* ts_effUnit(effLambda, unit, effLambda_, 'rb', ft(f, t)) = (ts_unit(unit, rb__, f, t) + ts_unit(unit, rb_, f, t)) / 2; // Average the 'rb' between the found data points.
* ts_effUnit(effLambda, unit, effLambda_, 'slope', ft(f, t)) = (1 / ts_unit(unit, eff__, f, t) + 1 / ts_unit(unit, eff_, f, t)) / 2; // Average the 'eff' between the found data points.
* );
* );
* );
* );
* );
* );
); // END LOOP OVER effLevel
); // END LOOP OVER unit
// Calculate unit wide parameters for each efficiency group
loop(unit,
loop(effLevel${mSettingsEff(mSolve, effLevel)},
loop(effLevelGroupUnit(effLevel, effGroup, unit),
ts_effGroupUnit(effGroup, unit, 'rb', ft(f, t))${sum(effSelector, ts_effUnit(effSelector, unit, 'rb', f, t))} = smax(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), ts_effUnit(effSelector, unit, 'rb', f, t));
ts_effGroupUnit(effGroup, unit, 'lb', ft(f, t))${sum(effSelector, ts_effUnit(effSelector, unit, 'lb', f, t))} = smin(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), ts_effUnit(effSelector, unit, 'lb', f, t));
ts_effGroupUnit(effGroup, unit, 'rb', ft(f, t))${sum(effSelector, ts_effUnit(effGroup, unit, effSelector, 'rb', f, t))} = smax(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), ts_effUnit(effGroup, unit, effSelector, 'rb', f, t));
ts_effGroupUnit(effGroup, unit, 'lb', ft(f, t))${sum(effSelector, ts_effUnit(effGroup, unit, effSelector, 'lb', f, t))} = smin(effSelector${effGroupSelectorUnit(effGroup, unit, effSelector)}, ts_effUnit(effGroup, unit, effSelector, 'lb', f, t));
ts_effGroupUnit(effGroup, unit, 'slope', ft(f, t))${sum(effSelector, ts_effUnit(effGroup, unit, effSelector, 'slope', f, t))} = smin(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector), ts_effUnit(effGroup, unit, effSelector, 'slope', f, t));
);
);
);
......
......@@ -5,7 +5,11 @@ Sets // Model related selections
counter "general counter set" /c000*c999/
rb "Right borders of efficiency curves"
/rb00*rb12/
/rb00*rb12/ // IMPORTANT! Has to equal the same param_unit!
eff "Effiency data indeces"
/eff00*eff12/ // IMPORTANT! Has to equal the same param_unit!
lambda "Lambda approximation indeces"
/lambda01*lambda12/ // IMPORTANT! Has to equal effLambda!
effSelector "Select equations and lambdas/slope for efficiency calculations"
/ directOff, directOn, lambda01*lambda12 /
effDirect(effSelector) "Using direct input to output equation"
......@@ -16,6 +20,8 @@ Sets // Model related selections
/ directOn /
effLambda(effSelector) "Lambdas in use for part-load efficiency representation"
/ lambda01*lambda12 /
effOnline(effSelector) "Efficiency selectors that use online variables"
/ directOn, lambda01*lambda12 / // IMPORTANT! Online variables are generated based on this, so keep it up to date!
effLevel "Pre-defined levels for efficiency representation that can start from t_solve + x"
/ level1*level9 /
;
......
......@@ -15,7 +15,6 @@ Sets
unit_flow(unit) "Unit that depend directly on variable energy flows (RoR, solar PV, etc.)"
unit_fuel(unit) "Units using a commercial fuel"
unit_minLoad(unit) "Units that have unit commitment restrictions (e.g. minimum power level)"
unit_online(unit) "Units that have an online variable"
unit_aggregate(unit) "Aggregate units aggragating several units"
unit_noAggregate(unit) "Units that are not aggregated at all"
unit_slope(unit) "Units with piecewise linear efficiency constraints"
......@@ -26,7 +25,6 @@ Sets
uFuel(unit, param_fuel, fuel) "Units linked with fuels"
unittype "Unit technology types"
* --- Nodes -----------------------------------------------------------------
* node_reservoir(node) "Hydropower reservoirs"
node_spill(node) "Nodes that can spill; used to remove v_spill variables where not relevant"
......@@ -88,17 +86,6 @@ Sets
msft(mType, s, f, t) "Combination of samples, forecasts and time periods in the models"
mftStart(mType, f, t) "Start point of simulation"
mftBind(mType, f, t) "Time periods/slices where forecasts/samples are coupled, note: t couples samples"
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"
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"
suft(effSelector, unit, f, t) "Selecting conversion efficiency equations"
sufts(effSelector, unit, f, t, effSelector) "Selecting conversion efficiency equations"
effGroup(effSelector) "Group name for efficiency selector set, e.g. Lambda02 contains Lambda01 and Lambda02"
effGroupSelector(effSelector, effSelector) "Group name for efficiency selector set, e.g. Lambda02 contains Lambda01 and Lambda02"
effLevelGroupUnit(effLevel, effSelector, unit) "What efficiency selectors are in use for each unit at each efficiency representation level"
effLevelSelectorUnit(effLevel, effSelector, unit) "What efficiency selectors are in use for each unit at each efficiency representation level"
effGroupSelectorUnit(effSelector, unit, effSelector) "Group name for efficiency selector set, e.g. Lambda02 contains Lambda01 and Lambda02"
fRealization(f) "fRealization of the forecasts"
fCentral(f) "Forecast that continues as sample(s) after the forecast horizon ends"
sInitial(s) "Sample that presents the realized/forecasted period"
......@@ -107,9 +94,19 @@ 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"
;
* --- 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"
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"
suft(effSelector, unit, f, t) "Selecting conversion efficiency equations"
sufts(effSelector, unit, f, t, effSelector) "Selecting conversion efficiency equations"
effGroup(effSelector) "Group name for efficiency selector set, e.g. DirectOff and Lambda02"
effGroupSelector(effSelector, effSelector) "Efficiency selectors included in efficiency groups, e.g. Lambda02 contains Lambda01 and Lambda02."
effLevelGroupUnit(effLevel, effSelector, unit) "What efficiency selectors are in use for each unit at each efficiency representation level"
effGroupSelectorUnit(effSelector, unit, effSelector) "Group name for efficiency selector set, e.g. Lambda02 contains Lambda01 and Lambda02"
;
* Set initial values to avoid errors when checking if parameter contents have been loaded from input data
fRealization('f00') = yes;
ms(mType, s) = no;
......@@ -124,6 +121,13 @@ alias(unit, unit_);
alias(node, from_node, to_node, node_, node_input, node_output);
alias(node, from_node, to_node);
alias(effSelector, effSelector_);
alias(effDirect, effDirect_);
alias(effDirectOff, effDirectOff_);
alias(effDirectOn, effDirectOn_);
alias(effLambda, effLambda_);
alias(lambda, lambda_, lambda__);
alias(rb, rb_, rb__);
alias(eff, eff_, eff__);
*if(active('rampSched'),
......
......@@ -27,11 +27,11 @@ Parameters
p_gnPolicy(grid, node, param_policy, *) "Policy data for grid, node"
p_fuelEmission(fuel, emission) "Fuel emission content"
p_uFuel(unit, param_fuel, fuel, param_unitFuel) "Parameters interacting between units and fuels"
p_effUnit(effSelector, unit, *) "Data for piece-wise linear efficiency blocks"
p_effUnit(effSelector, unit, effSelector, *) "Data for piece-wise linear efficiency blocks"
p_effGroupUnit(effSelector, unit, *) "Unit data specific to a efficiency group (e.g. left border of the unit)"
// Time dependent unit & fuel parameters
ts_unit(unit, *, f, t) "Time dependent unit data, where energy type doesn't matter"
ts_effUnit(effSelector, unit, *, f, t) "Time dependent data for piece-wise linear efficiency blocks"
ts_effUnit(effSelector, unit, effSelector, *, f, t) "Time dependent data for piece-wise linear efficiency blocks"
ts_effGroupUnit(effSelector, unit, *, f, t) "Time dependent efficiency group unit data"
// Alias used for interval aggregation
ts_unit_(unit, *, f, t)
......
......@@ -59,7 +59,6 @@ gn2gnu(grid_, node_input, grid, node, unit)$(gnu_input(grid_, node_input, unit)
nu(node, unit)$sum(grid, gnu(grid, node, unit)) = yes;
nuRescapable(restype, resdirection, node, unit)$p_nuReserves(node, unit, restype, resdirection) = yes;
unit_minload(unit)$[p_unit(unit, 'rb00') > 0 and p_unit(unit, 'rb00') < 1] = yes; // If the first point is between 0 and 1, then the unit has a min load limit
unit_online(unit)$[p_unit(unit, 'rb00') > 0 or p_unit(unit, 'startupCost') or p_unit(unit, 'startupFuelCons') or p_unit(unit, 'coldStart') ] = yes; // How does this differ from unit_minLoad, exactly?
unit_flow(unit)$sum(flow, flowUnit(flow, unit)) = yes;
unit_fuel(unit)$sum[ (fuel, node)$sum(t, ts_fuelPriceChangenode(fuel, node, t)), uFuel(unit, 'main', fuel) ] = yes;
unit_elec(unit)$sum(gnu(grid, node, unit), p_gnu('elec', node, unit, 'maxGen')) = yes;
......@@ -88,11 +87,29 @@ p_gn(gn(grid, node), 'energyStoredPerUnitOfState')$(not p_gn(grid, node, 'energy
* --- Perform various data checks, and abort if errors are detected -----------
* Check the integrity of efficiency approximation related data
tmp = 0; // Log the unit index for finding the error easier.
loop( unit,
tmp = tmp + 1; // Increase the unit counter
// Check that 'rb' is defined correctly
count = 0; // Initialize the previous rb to zero
loop( rb,
abort${p_unit(unit, rb) + 1${not p_unit(unit, rb)} < count} "param_unit 'rb's must be defined as zero or positive and increasing!";
count = p_unit(unit, rb);
);
// Check that efficiency approximations have sufficient data
loop( effLevelGroupUnit(effLevel, effSelector, unit),
loop( rb__${p_unit(unit, rb__) = smax(rb, p_unit(unit, rb))}, // Loop over the 'rb's to find the last defined data point.
// Lambda
loop( lambda${sameas(lambda, effSelector)}, // Loop over the lambdas to find the 'magnitude' of the approximation
if(ord(lambda) > ord(rb__), put log '!!! Error occurred on unit #' tmp); // Display unit that causes error, NEEDS WORK
abort${ord(lambda) > ord(rb__)} "Order of the lambda approximation cannot exceed the number of efficiency data points!"
);
// DirectOn
loop( rb_${p_unit(unit, rb_) = smin(rb${p_unit(unit, rb)}, p_unit(unit, rb))}, // Loop over the 'rb's to find the first nonzero 'rb' data point.
if(effDirectOn(effSelector) AND ord(rb__) = ord(rb_), put log '!!! Error occurred on unit #' tmp); // Display unit that causes error, NEEDS WORK
abort${effDirectOn(effSelector) AND ord(rb__) = ord(rb_)} "directOn requires two efficiency data points with nonzero 'rb'!"
);
);
);
);
......@@ -60,21 +60,21 @@ q_obj ..
)
)
// Start-up costs
+ sum(uft_online(unit_online, f, t),
+ sum(uft_online(unit, f, t),
+ {
+ v_startup(unit_online, f, t) // Cost of starting up
- sum(t_${mftStart(m, f, t_) and uft_online(unit_online, f, t_)}, 0.5 * v_online(unit_online, f, t_)) // minus value of avoiding startup costs before
- sum(t_${mftLastSteps(m, f, t_) and uft_online(unit_online, f, t_)}, 0.5 * v_online(unit_online, f, t_)) // or after the model solve
} / p_unit(unit_online, 'unitCount')
+ v_startup(unit, f, t) // Cost of starting up
- sum(t_${mftStart(m, f, t_) and uft_online(unit, f, t_)}, 0.5 * v_online(unit, f, t_)) // minus value of avoiding startup costs before
- sum(t_${mftLastSteps(m, f, t_) and uft_online(unit, f, t_)}, 0.5 * v_online(unit, f, t_)) // or after the model solve
} / p_unit(unit, 'unitCount')
* {
// Startup variable costs
+ p_unit(unit_online, 'startupCost')
* p_unit(unit_online, 'unitCapacity')
+ p_unit(unit, 'startupCost')
* p_unit(unit, 'unitCapacity')
// Start-up fuel and emission costs
+ sum(uFuel(unit_fuel, 'startup', fuel),
+ p_unit(unit_online, 'startupFuelCons')
* p_unit(unit_online, 'unitCapacity')
* sum(gnu_output(grid, node, unit_online),
+ p_unit(unit, 'startupFuelCons')
* p_unit(unit, 'unitCapacity')
* sum(gnu_output(grid, node, unit),
// Fuel costs for start-up fuel use
+ ( + sum{tFuel$[ord(tFuel) <= ord(t)],
ts_fuelPriceChangenode(fuel, node, tFuel) }
......@@ -83,8 +83,8 @@ q_obj ..
p_fuelEmission(fuel, emission) / 1e3
* p_gnPolicy(grid, node, 'emissionTax', emission) // Sum emission costs from different output energy types
)
) / p_gnu(grid, node, unit_online, 'maxGen') // Calculate these in relation to maximum output ratios between multiple outputs
) * sum(gnu_output(grid, node, unit_online), p_gnu(grid, node, unit_online, 'maxGen')) // see line above
) / p_gnu(grid, node, unit, 'maxGen') // Calculate these in relation to maximum output ratios between multiple outputs
) * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'maxGen')) // see line above
)
}
)
......@@ -246,17 +246,17 @@ q_maxUpward(gnuft(grid, node, unit, f, t))${ [uft_online(unit, f, t) and p_
+ v_gen.up(grid, node, unit, f, t) * [ (v_online(unit, f, t) / p_unit(unit, 'unitCount'))$uft_online(unit, f, t) + 1$(not uft_online(unit, f, t)) ]
;
* -----------------------------------------------------------------------------
q_startup(uft_online(unit_online, ft_dynamic(f, t))) ..
+ v_startup(unit_online, f+pf(f,t), t+pt(t))
q_startup(uft_online(unit, ft_dynamic(f, t))) ..
+ v_startup(unit, f+pf(f,t), t+pt(t))
=G=
+ v_online(unit_online, f, t)
- v_online(unit_online, f+pf(f,t), t+pt(t)) // This reaches to tFirstSolve when pt = -1
+ v_online(unit, f, t)
- v_online(unit, f+pf(f,t), t+pt(t)) // This reaches to tFirstSolve when pt = -1
;
* -----------------------------------------------------------------------------
q_bindOnline(unit_online, mftBind(m, f, t))${uft_online(unit_online, f, t)} ..
+ v_online(unit_online, f, t)
q_bindOnline(unit, mftBind(m, f, t))${uft_online(unit, f, t)} ..
+ v_online(unit, f, t)
=E=
+ v_online(unit_online, f+mft_bind(m,f,t), t+mt_bind(m,t))$uft_online(unit_online, f+mft_bind(m,f,t), t+mt_bind(m,t))
+ v_online(unit, f+mft_bind(m,f,t), t+mt_bind(m,t))$uft_online(unit, f+mft_bind(m,f,t), t+mt_bind(m,t))
;
* -----------------------------------------------------------------------------
q_conversionDirectInputOutput(suft(effDirect, unit, f, t)) ..
......@@ -269,14 +269,14 @@ q_conversionDirectInputOutput(suft(effDirect, unit, f, t)) ..
=E=
+ sum(gnu_output(grid, node, unit),
+ v_gen(grid, node, unit, f, t)
* (p_effUnit(effDirect, unit, 'slope')${not ts_effUnit(effDirect, unit, 'slope', f, t)} + ts_effUnit(effDirect, unit, 'slope', f, t))
* (p_effUnit(effDirect, unit, effDirect, 'slope')${not ts_effUnit(effDirect, unit, effDirect, 'slope', f, t)} + ts_effUnit(effDirect, unit, effDirect, 'slope', f, t))
)
+ v_online(unit, f, t)${uft_online(unit, f, t)}
/ p_unit(unit, 'unitCount')
* sum(gnu_output(grid, node, unit),
+ p_gnu(grid, node, unit, 'maxGen')
)
* (p_effUnit(effDirect, unit, 'section')${not ts_effUnit(effDirect, unit, 'section', f, t)} + ts_effUnit(effDirect, unit, 'section', f, t))
* (p_effUnit(effDirect, unit, effDirect, 'section')${not ts_effUnit(effDirect, unit, effDirect, 'section', f, t)} + ts_effUnit(effDirect, unit, effDirect, 'section', f, t))
;
* -----------------------------------------------------------------------------
q_conversionSOS2InputIntermediate(suft(effGroup, unit, f, t))$effLambda(effGroup) ..
......@@ -289,8 +289,8 @@ q_conversionSOS2InputIntermediate(suft(effGroup, unit, f, t))$effLambda(effGroup
=E=
+ sum(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector),
+ v_sos2(unit, f, t, effSelector)
* (p_effUnit(effSelector, unit, 'rb')${not ts_effUnit(effSelector, unit, 'rb', f, t)} + ts_effUnit(effSelector, unit, 'rb', f, t))
* (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f, t)} + ts_effUnit(effSelector, unit, 'slope', f, t))
* (p_effUnit(effGroup, unit, effSelector, 'rb')${not ts_effUnit(effGroup, unit, effSelector, 'rb', f, t)} + ts_effUnit(effGroup, unit, effSelector, 'rb', f, t))
* (p_effUnit(effGroup, unit, effSelector, 'slope')${not ts_effUnit(effGroup, unit, effSelector, 'slope', f, t)} + ts_effUnit(effGroup, unit, effSelector, 'slope', f, t))
)
/ p_unit(unit, 'unitCount')
* sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'maxGen'))
......@@ -308,7 +308,7 @@ q_conversionSOS2Constraint(suft(effGroup, unit, f, t))$effLambda(effGroup) ..
q_conversionSOS2IntermediateOutput(suft(effGroup, unit, f, t))$effLambda(effGroup) ..
+ sum(effSelector$effGroupSelectorUnit(effGroup, unit, effSelector),
+ v_sos2(unit, f, t, effSelector)
* (p_effUnit(effSelector, unit, 'rb')${not ts_effUnit(effSelector, unit, 'rb', f, t)} + ts_effUnit(effSelector, unit, 'rb', f, t))
* (p_effUnit(effGroup, unit, effSelector, 'rb')${not ts_effUnit(effGroup, unit, effSelector, 'rb', f, t)} + ts_effUnit(effGroup, unit, effSelector, 'rb', f, t))
)
/ p_unit(unit, 'unitCount')
* sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'maxGen'))
......@@ -375,14 +375,14 @@ q_stateUpwardLimit(gn_state(grid, node), m, ft_dynamic(f, t))$( sum(gn2gnu(gr
+ sum(gn2gnu(grid_, node_input, grid, node, unit)${uft(unit, f+pf(f,t), t+pt(t))},
+ sum(restype$nuRescapable(restype, 'resDown', node_input, unit), // Downward reserves from units that output energy to the node
+ v_reserve(restype, 'resDown', node_input, unit, f+pf(f,t), t+pt(t))
/ smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
/ sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
)
// Reserve provision from units that take input from this node
+ sum(gn2gnu(grid, node, grid_, node_output, unit)${uft(unit, f+pf(f,t), t+pt(t))},
+ sum(restype$nuRescapable(restype, 'resDown', node_output, unit), // Downward reserves from units that use the node as energy input
+ v_reserve(restype, 'resDown', node_output, unit, f+pf(f,t), t+pt(t))
* smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
* sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
)
// Here we could have a term for using the energy in the node to offer reserves as well as imports and exports of reserves, but as long as reserves are only
......@@ -412,14 +412,14 @@ q_stateDownwardLimit(gn_state(grid, node), m, ft_dynamic(f, t))$( sum(gn2gnu(
+ sum(gn2gnu(grid_, node_input, grid, node, unit)${uft(unit, f+pf(f,t), t+pt(t))},
+ sum(restype$nuRescapable(restype, 'resUp', node_input, unit), // Upward reserves from units that output energy to the node
+ v_reserve(restype, 'resUp', node_input, unit, f+pf(f,t), t+pt(t))
/ smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
/ sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
)
// Reserve provision from units that take input from this node
+ sum(gn2gnu(grid, node, grid_, node_output, unit)${uft(unit, f+pf(f,t), t+pt(t))},
+ sum(restype$nuRescapable(restype, 'resUp', node_output, unit), // Upward reserves from units that use the node as energy input
+ v_reserve(restype, 'resUp', node_output, unit, f+pf(f,t), t+pt(t))
* smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
* sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
)
// Here we could have a term for using the energy in the node to offer reserves as well as imports and exports of reserves, but as long as reserves are only
......@@ -436,12 +436,12 @@ q_boundState(gnn_boundState(grid, node, node_), m, ft_dynamic(f, t)) ..
// Upwards reserve provided by input units
- sum(nuRescapable(restype, 'resUp', node_input, unit)${sum(grid_, gn2gnu(grid_, node_input, grid, node, unit)) AND uft(unit, f+pf(f,t), t+pt(t))},
+ v_reserve(restype, 'resUp', node_input, unit, f+pf(f,t), t+pt(t))
/ smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
/ sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
// Upwards reserve providewd by output units
- sum(nuRescapable(restype, 'resUp', node_output, unit)${sum(grid_, gn2gnu(grid, node, grid_, node_output, unit)) AND uft(unit, f+pf(f,t), t+pt(t))},
+ v_reserve(restype, 'resUp', node_output, unit, f+pf(f,t), t+pt(t))
/ smax(effSelector${suft(effSelector, unit, f+pf(f,t), t+pt(t))}, (p_effUnit(effSelector, unit, 'slope')${not ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effUnit(effSelector, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
/ sum(effGroup${suft(effGroup, unit, f+pf(f,t), t+pt(t))}, (p_effGroupUnit(effGroup, unit, 'slope')${not ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t))} + ts_effGroupUnit(effGroup, unit, 'slope', f+pf(f,t), t+pt(t)))) // Efficiency approximated using maximum slope of effGroup?
)
// Here we could have a term for using the energy in the node to offer reserves as well as imports and exports of reserves, but as long as reserves are only
// considered in power grids that do not have state variables, these terms are not needed. Earlier commit (16.2.2017) contains a draft of those terms.
......@@ -458,12 +458,12 @@ q_boundState(gnn_boundState(grid, node, node_), m, ft_dynamic(f, t)) ..
// Possible reserve by input node
+ sum(nuRescapable(restype, 'resDown', node_input, unit)${sum