Commit dac28dc5 authored by Juha Kiviluoma's avatar Juha Kiviluoma
Browse files

Merge remote-tracking branch 'origin/dev' into #107_inputs_outputs

# Conflicts:
#	inc/2d_constraints.gms
parents 6685500f d8ccc397
# Changelog
All notable changes to this project will be documented in this file.
## [Unreleased]
### Changed
- Static inertia requirement can be fulfilled by both rotational inertia of machines and certain reserve products
## [1.2.1] - 2019-11-26
### Fixed
- Fixed a possible division by zero in the calculation of r_gnuUtilizationRate
......
......@@ -142,8 +142,8 @@ if (mType('invest'),
* --- Control the solver ------------------------------------------------------
// Control the use of advanced basis
mSettings('invest', 'loadPoint') = 2; // 0 = no basis, 1 = latest solve, 2 = all solves, 3 = first solve
mSettings('invest', 'savePoint') = 2; // 0 = no basis, 1 = latest solve, 2 = all solves, 3 = first solve
mSettings('invest', 'loadPoint') = 0; // 0 = no basis, 1 = latest solve, 2 = all solves, 3 = first solve
mSettings('invest', 'savePoint') = 0; // 0 = no basis, 1 = latest solve, 2 = all solves, 3 = first solve
); // END if(mType)
......
......@@ -223,10 +223,8 @@ param_gn "Possible parameters for grid, node" /
boundEnd "A flag to bound last t in each solve based on the reference constant or time series"
boundAll "A flag to bound the state to the reference in all time steps"
boundStartToEnd "Force the last states to equal the first state"
forecastLength "Length of forecasts in use for the node (hours). After this, the node will use the central forecast."
* forecastLength "Length of forecasts in use for the node (hours). After this, the node will use the central forecast." // NOT IMPLEMENTED
capacityMargin "Capacity margin used in invest mode (MW)"
* defaultFrequency "default frequency for each node"
* ROCOF "Rate of change of frequency"
/
param_gnBoundaryTypes "Types of boundaries that can be set for a node with a state variable" /
......@@ -243,7 +241,7 @@ param_gnBoundaryTypes "Types of boundaries that can be set for a node with a sta
param_gnBoundaryProperties "Properties that can be set for the different boundaries" /
useTimeSeries "A flag to use time series to set state bounds and limits"
useConstant "A flag to use constant to set state bounds and limits"
deltaFromReference "The constant or the time series indicate how much the boundary deviates from reference (instead of being an absolute number)"
* deltaFromReference "The constant or the time series indicate how much the boundary deviates from reference (instead of being an absolute number)" // NOT IMPLEMENTED
constant "A constant value for the boundary or the reference"
slackCost "The cost of exceeding the slack boundary"
multiplier "A multiplier to change the value (either constant or time series), default 1"
......@@ -287,6 +285,7 @@ param_gnuBoundaryProperties "Properties that can be set for the different bounda
/
param_unit "Set of possible data parameters for units" /
// Given in input data
unitCount "Number of subunits if aggregated"
outputCapacityTotal "Output capacity of the unit, calculated by summing all the outputs together by default, unless defined in data"
unitOutputCapacityTotal "Output capacity of the unit, calculated by summing all the subunit output sizes together by default"
......@@ -307,10 +306,10 @@ param_unit "Set of possible data parameters for units" /
rampSpeedFromMinLoad "Ramping speed from shutdown decision to zero load (p.u./min)"
minOperationHours "Minimum operation time (h), prevents shutdown after startup until the defined amount of time has passed"
minShutdownHours "Minimum shut down time (h), prevents starting up again after the defined amount of time has passed"
SO2 "SO2 emissions (tonne per MWh_fuel)"
NOx "NOx emissions (tonne per MWh_fuel)"
CH4 "CH4 emissions (tonne per MWh_fuel)"
resTimelim "How long should a storage be able to provide reserve (h)"
* SO2 "SO2 emissions (tonne per MWh_fuel)" // NOT IMPLEMENTED
* NOx "NOx emissions (tonne per MWh_fuel)" // NOT IMPLEMENTED
* CH4 "CH4 emissions (tonne per MWh_fuel)" // NOT IMPLEMENTED
* resTimelim "How long should a storage be able to provide reserve (h)" // NOT IMPLEMENTED
eff00 * eff12 "Efficiency of the unit to convert input to output/intermediate product"
opFirstCross "The operating point where the real efficiency curve and approximated efficiency curve cross"
op00 * op12 "Right border of the efficiency point"
......@@ -323,6 +322,7 @@ param_unit "Set of possible data parameters for units" /
investMIP "A flag to make integer investment instead of continous investment"
maxUnitCount "Maximum number of units when making integer investments"
minUnitCount "Minimum number of units when making integer investments"
// Calculated based on other input data
lastStepNotAggregated "Last time step when the unit is not yet aggregated - calculated in inputsLoop.gms for units that have aggregation"
/
......@@ -348,32 +348,31 @@ param_unitStartupfuel "Parameters for startup fuel limits in units" /
fixedFuelFraction "Fixed share of a fuel in the consumption mix" //only for start-up fuels
/
param_policy "Set of possible data parameters for grid, node, regulation" /
param_policy "Set of possible data parameters for groups or grid, node, regulation" /
emissionTax "Emission tax (EUR/tonne)"
emissionCap "Emission limit (tonne)"
instantaneousShareMax "Maximum instantaneous share of generation and import from a particular group of units and transfer links"
energyShareMax "Maximum energy share of generation from a particular group of units"
energyShareMin "Minimum energy share of generation from a particular group of units"
kineticEnergyMin "Minimum system kinetic energy (MWs)"
constrainedCapMultiplier "Multiplier a(i) for unit investments in equation Sum(i, a(i)*v_invest(i)) <= b"
constrainedCapTotalMax "Total maximum b for unit investments in equation Sum(i, a(i)*v_invest(i)) <= b"
constrainedOnlineMultiplier "Multiplier a(i) for online units in equation Sum(i, a(i)*v_online(i)) <= b"
constrainedOnlineTotalMax "Total maximum b for online units in equation Sum(i, a(i)*v_online(i)) <= b"
minCons "minimum consumption of storage unit when charging"
* minCons "minimum consumption of storage unit when charging" // NOT USED, PENDING REMOVAL
ROCOF "Maximum rate of change of frequency (Hz/s)"
defaultFrequency "Nominal frequency in the system (Hz)"
// Reserve related parameters, currently without a proper parameter set
update_frequency "Frequency of updating reserve contributions"
update_offset "Optional offset for delaying the reserve update frequency"
gate_closure "Number of timesteps ahead of dispatch that reserves are fixed"
use_time_series "Flag for using time series data. !!! REDUNDANT WITH useTimeseries, PENDING REMOVAL !!!"
* use_time_series "Flag for using time series data. !!! REDUNDANT WITH useTimeseries, PENDING REMOVAL !!!"
reserve_length "Length of reserve horizon"
reserveReliability "Reliability parameter of reserve provisions"
reserve_increase_ratio "Unit output is multiplied by this factor to get the increase in reserve demand"
portion_of_infeed_to_reserve "Proportion of the generation of a tripping unit that needs to be covered by reserves from other units"
offlineReserveCapability "Proportion of an offline unit which can contribute to a category of reserve"
ReserveShareMax "Maximum reserve share of a group of units"
LossOfTrans
LossOfTrans "A flag to tell that N-1 reserve is needed due to a possibility that an interconnector to/from the node group fails"
/
* --- Efficiency Approximation Related Sets -----------------------------------
......
......@@ -87,6 +87,7 @@ Sets
restypeReleasedForRealization(restype) "Reserve types that are released for the realized time intervals"
offlineRes (restype) "Reserve types where offline reserve provision possible"
offlineResUnit (unit) "Units where offline reserve provision possible"
restype_inertia(restype) "Reserve types where the requirement can also be fulfilled with the inertia of synchronous machines"
* --- Sets to define time, forecasts and samples ------------------------------
$$include '%input_dir%/timeAndSamples.inc'
......
......@@ -43,6 +43,7 @@ $ifthen exist '%input_dir%/inputData.gdx'
$$loaddc restype
$$loaddc restypeDirection
$$loaddc restypeReleasedForRealization
$$loaddc restype_inertia
$$loaddc p_groupReserves
$$loaddc p_groupReserves3D
$$loaddc p_groupReserves4D
......
......@@ -242,6 +242,18 @@ $ifthen exist '%input_dir%/restypeReleasedForRealization3.gdx'
$$gdxin
$endif
$ifthen exist '%input_dir%/restype_inertia2.gdx'
$$gdxin '%input_dir%/restype_inertia2.gdx'
$$loaddcm restype_inertia
$$gdxin
$endif
$ifthen exist '%input_dir%/restype_inertia3.gdx'
$$gdxin '%input_dir%/restype_inertia3.gdx'
$$loaddcm restype_inertia
$$gdxin
$endif
$ifthen exist '%input_dir%/p_groupReserves2.gdx'
$$gdxin '%input_dir%/p_groupReserves2.gdx'
$$loaddcm p_groupReserves
......
......@@ -107,7 +107,7 @@ equations
q_boundCyclic(grid, node, s, s, mType) "Cyclic node state bound for the first and the last states of samples"
// Policy
q_inertiaMin(group, s, f, t) "Minimum inertia in a group of nodes"
q_inertiaMin(restype, up_down, group, s, f, t) "Minimum inertia in a group of nodes"
q_instantaneousShareMax(group, s, f, t) "Maximum instantaneous share of generation and controlled import from a group of units and links"
q_constrainedOnlineMultiUnit(group, s, f, t) "Constrained number of online units for a group of units"
q_capacityMargin(grid, node, s, f, t) "There needs to be enough capacity to cover energy demand plus a margin"
......
......@@ -95,6 +95,7 @@ q_resDemand(restypeDirectionGroup(restype, up_down, group), sft(s, f, t))
${ ord(t) < tSolveFirst + p_groupReserves(group, restype, 'reserve_length')
and not [ restypeReleasedForRealization(restype)
and sft_realized(s, f, t)]
and not restype_inertia(restype)
} ..
// Reserve provision by capable units on this group
......@@ -2498,30 +2499,54 @@ q_boundCyclic(gnss_bound(gn_state(grid, node), s_, s), m)
*--- Minimum Inertia ----------------------------------------------------------
q_inertiaMin(group, sft(s, f, t))
${ p_groupPolicy(group, 'kineticEnergyMin')
q_inertiaMin(restypeDirectionGroup(restype_inertia, up_down, group), sft(s, f, t))
${ ord(t) < tSolveFirst + p_groupReserves(group, restype_inertia, 'reserve_length')
and not [ restypeReleasedForRealization(restype_inertia)
and sft_realized(s, f, t)]
and p_groupPolicy(group, 'ROCOF')
and p_groupPolicy(group, 'defaultFrequency')
} ..
// Kinetic energy in the system
+ sum(gnu(grid, node, unit)${ p_gnu(grid, node, unit, 'unitSize')
and gnGroup(grid, node, group)
// Rotational energy in the system
+ p_groupPolicy(group, 'ROCOF')*2
* [
+ sum(gnu(grid, node, unit)${ p_gnu(grid, node, unit, 'unitSize')
and gnuft(grid, node, unit, f, t)
},
+ p_gnu(grid, node, unit, 'inertia')
* p_gnu(grid ,node, unit, 'unitSizeMVA')
* [
+ v_online_LP(unit, s, f+df_central(f,t), t)
${uft_onlineLP(unit, f, t)}
+ v_online_MIP(unit, s, f+df_central(f,t), t)
${uft_onlineMIP(unit, f, t)}
+ v_gen(grid, node, unit, s, f, t)${not uft_online(unit, f, t)}
/ p_gnu(grid, node, unit, 'unitSize')
] // * p_gnu
) // END sum(gnu_output)
},
+ p_gnu(grid, node, unit, 'inertia')
* p_gnu(grid ,node, unit, 'unitSizeMVA')
* [
+ v_online_LP(unit, s, f+df_central(f,t), t)
${uft_onlineLP(unit, f, t)}
+ v_online_MIP(unit, s, f+df_central(f,t), t)
${uft_onlineMIP(unit, f, t)}
+ v_gen(grid, node, unit, s, f, t)${not uft_online(unit, f, t)}
/ (p_gnu(grid, node, unit, 'unitSize')$gnu_output(grid, node, unit) - p_gnu(grid, node, unit, 'unitSize')$gnu_input(grid, node, unit))
] // * p_gnu
) // END sum(gnu)
] // END * p_groupPolicy
=G=
+ p_groupPolicy(group, 'kineticEnergyMin')
// Demand for rotational energy / fast frequency reserve
+ p_groupPolicy(group, 'defaultFrequency')
* [
+ p_groupReserves(group, restype_inertia, up_down)
- sum(gnuft(grid, node, unit, f, t)${ gnGroup(grid, node, group)
and gnuRescapable(restype_inertia, up_down, grid, node, unit)
},
+ v_reserve(restype_inertia, up_down, grid, node, unit, s, f+df_reserves(grid, node, restype_inertia, f, t), t)
* [ // Account for reliability of reserves
+ 1${sft_realized(s, f+df_reserves(grid, node, restype_inertia, f, t), t)} // reserveReliability limits the reliability of reserves locked ahead of time.
+ p_gnuReserves(grid, node, unit, restype_inertia, 'reserveReliability')${not sft_realized(s, f+df_reserves(grid, node, restype_inertia, f, t), t)}
] // END * v_reserve
) // END sum(gnuft)
// Reserve demand feasibility dummy variables
- vq_resDemand(restype_inertia, up_down, group, s, f+df_reservesGroup(group, restype_inertia, f, t), t)
- vq_resMissing(restype_inertia, up_down, group, s, f+df_reservesGroup(group, restype_inertia, f, t), t)${
ft_reservesFixed(group, restype_inertia, f+df_reservesGroup(group, restype_inertia, f, t), t)}
] // END * p_groupPolicy
;
*--- Maximum Share of Instantaneous Generation --------------------------------
......
......@@ -677,11 +677,13 @@ loop(m, // Not ideal, but multi-model functionality is not yet implemented
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_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!";
// Check if the first interval is long enough for proper commitment of reserves in the schedule model
if(sameas(m, 'schedule'),
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 if
); // END loop(restypeDirectionGroup)
......@@ -700,17 +702,28 @@ loop(m, // Not ideal, but multi-model functionality is not yet implemented
put log '!!! Warning: Trajectories used on aggregated time steps! This could result in significant distortion of the trajectories.';
); // END if()
* --- Check that the first interval block is compatible with t_jump' ----------
* --- Check if 't_trajectoryHorizon' is long enough -----
if (mod(mSettings(m, 't_jump'), mInterval(m, 'stepsPerInterval', 'c000')) <> 0,
put log '!!! Abort: t_jump should be divisible by the first interval!' /;
abort "'t_jump' should be divisible by the first interval!";
if ((mSettings(m, 't_trajectoryHorizon') < mSettings(m, 't_jump') + smax(unit, p_u_runUpTimeIntervalsCeil(unit))
OR mSettings(m, 't_trajectoryHorizon') < mSettings(m, 't_jump') + smax(unit, p_u_shutdownTimeIntervalsCeil(unit)))
AND mSettings(m, 't_trajectoryHorizon') ne 0,
put log '!!! Abort: t_trajectoryHorizon should be at least as long as t+jump + max trajectory.';
abort "t_trajectoryHorizon should be at least as long as t+jump + max trajectory. This may lead to infeasibilities";
); // END if()
if (mInterval(m, 'lastStepInIntervalBlock', 'c000') < mSettings(m, 't_jump'),
put log '!!! Abort: The first interval block should not be shorter than t_jump!' /;
abort "The first interval block should not be shorter than 't_jump'!";
); // END if()
* --- Check that the first interval block is compatible with 't_jump' in the schedule model -----
if(sameas(m, 'schedule'),
if (mod(mSettings(m, 't_jump'), mInterval(m, 'stepsPerInterval', 'c000')) <> 0,
put log '!!! Abort: t_jump should be divisible by the first interval!' /;
abort "'t_jump' should be divisible by the first interval!";
); // END if()
if (mInterval(m, 'lastStepInIntervalBlock', 'c000') < mSettings(m, 't_jump'),
put log '!!! Abort: The first interval block should not be shorter than t_jump!' /;
abort "The first interval block should not be shorter than 't_jump'!";
); // END if()
); // END if
); // END loop(m)
......@@ -25,7 +25,7 @@ $offtext
put_utility 'gdxin' / mSolve.tl:0 '_p.gdx';
execute_loadpoint;
elseif mSettings(mSolve, 'loadPoint') = 2,
put_utility 'gdxin' / 'schedule_p' solveCount:0:0 '.gdx';
put_utility 'gdxin' / mSolve.tl:0 '_p' solveCount:0:0 '.gdx';
execute_loadpoint;
elseif mSettings(mSolve, 'loadPoint') = 3 and solveCount = 1,
put_utility 'gdxin' / mSolve.tl:0 '_p.gdx';
......
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