Commit 0c2e229c authored by Juha Kiviluoma's avatar Juha Kiviluoma
Browse files

Changing the way constant and timeseries bounds on state variables are...

Changing the way constant and timeseries bounds on state variables are defined. Also updating the equations on state slack variables and reserve use with state variables.
parent d2ed5810
......@@ -209,3 +209,7 @@ loop(m,
);
);
* Set slack direction
p_slackDirection(upwardSlack) = 1;
p_slackDirection(downwardSlack) = -1;
......@@ -90,6 +90,9 @@ $onOrder
ft(f,t) = mft(mSolve, f, t);
ft_dynamic(f,t) = ft(f,t);
ft_dynamic(f,tSolve) = no;
ft_full(f,t) = no;
ft_full(f,t) = ft(f,t);
ft_full(f,t) = mftLastSteps(mSolve, f, t);
loop(counter$mInterval(mSolve, 'intervalLength', counter),
lastCounter = ord(counter);
);
......
......@@ -69,30 +69,52 @@ $if set rampSched active('rampSched') = %rampSched%;
* --- Set definitions for parameters -----------------------------------------------------
Sets
param_gn "Set of possible data parameters for grid, node" /
maxState "Absolute maximum state of the node (unit depends on energyCapacity)"
minState "Absolute minimum energy in the node (unit depends on energyCapacity)"
maxStateSlack "Maximum increase or decrease in the state of the node with a specifict cost co-efficient (unit depends on energyCapacity)"
referenceState "Reference value for a state that can be used to fix a state (unit depends on energyCapacity)"
energyCapacity "Energy capacity of the node (MWh/?, allows for changing the quality of the node state variables)"
maxSpill "Maximum spill rate from the node (MWh/h)"
minSpill "Minimum spill rate from the node (MWh/h)"
chargingEff "Average charging efficiency (p.u)"
param_gn "Possible parameters for grid, node" /
chargingEff "Average charging efficiency (p.u)"
dischargingEff "Average discharging efficiency (p.u.)"
selfDischargeLoss "Self discharge rate of the node (p.u.)"
fixNothing "A flag to indicate that no state should be fixed"
fixStart "A flag to fix tSolve based on fixState constant or time series or on the previous solve"
fixEnd "A flag to fix last t based on fixState constant or time series"
fixConstant "A flag to fix a state with a constant value in referenceState"
fixTimeSeries "A flag to use time series to fix states"
fixCircular "Force the last states to equal the first state"
maxUseTimeSeries "Use time series instead of a constant to set state maximum"
minUseTimeSeries "Use time series instead of a constant to set state minimum"
referenceMultiplier "A multiplier to change the reference value (either constant or time series), default 1"
maxMultiplier "State maximum (time series or constant) multiplier, default 1"
minMultiplier "State minimum (time series or constant) multiplier, default 1"
unitConversion "A possible unit conversion if v_state uses something else than MWh"
boundStart "A flag to bound the first t in the run using reference constant or time series"
boundStartAndEnd "A flag that both start and end are bound using reference constant or time series"
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"
/
param_gnBoundaryTypes "Types of boundaries that can be set for a node with a state variable" /
upwardLimit "Absolute maximum state of the node (unit depends on energyCapacity)"
downwardLimit "Absolute minimum energy in the node (unit depends on energyCapacity)"
upwardSlack01*upwardSlack20 "A threshold after which a specific cost co-efficient is applied (unit depends on energyCapacity)"
downwardSlack01*downwardSlack20 "A threshold after which a specific cost co-efficient is applied (unit depends on energyCapacity)"
reference "Reference value for a state that can be used to bound a state (unit depends on energyCapacity)"
maxSpill "Maximum spill rate from the node (MWh/h)"
minSpill "Minimum spill rate from the node (MWh/h)"
/
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)"
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"
/
slack(param_gnBoundaryTypes) "Categories for slack variables"
/ upwardSlack01*upwardSlack20, downwardSlack01*downwardSlack20 /
upwardSlack(slack) "Set of upward slacks"
/ upwardSlack01*upwardSlack20 /
downwardSlack(slack) "Set of downward slacks"
/ downwardSlack01*downwardSlack20 /
inc_dec "Increase or decrease in dummy or slack variables"
/ increase, decrease /
stateLimits(param_gnBoundaryTypes) "set of upward and downward state limits"
/ upwardLimit, downwardLimit /
spillLimits(param_gnBoundaryTypes) "set of upward and downward state limits"
/ maxSpill, minSpill /
useConstantOrTimeSeries(param_gnBoundaryProperties) "useTimeSeries and useConstant property together"
/ useTimeSeries, useConstant /
param_gnn "Set of possible data parameters for grid, node, node (nodal interconnections)" /
transferCap "Transfer capacity limits"
transferLoss "Transfer losses"
......@@ -160,9 +182,3 @@ param_union "Different ways inputs and outputs of energy conversion units can be
constrained "The usage is limited by the output of free outputs - in relation to the efficiency limits"
substitute "Inputs and outputs can be substituted"
/
param_slack "Possible parameters for node inc_dec penalties" /
costCoeff "The cost coefficient of the slack category to be used in the objective function"
maxSlack "The maximum slack provided"
/
......@@ -74,15 +74,6 @@ Sets
restypeDirectionNode(restype, resdirection, node) "Nodes with reserve requirements"
nuRescapable(restype, resdirection, node, unit) "Units capable and available to provide particular reserves"
* --- Feasibility control -----------------------------------------------------
slack "Categories for slack variables"
/ slack01*slack10 /
inc_dec "Increase or decrease in dummy or slack variables"
/ increase
decrease
/
gnSlack(inc_dec, slack, grid, node) "Grid nodes with a slack state variable"
* --- Sets to define time, forecasts and samples -----------------------------------------------
$$include 'input/timeAndSamples.inc'
m(mType) "model(s) in use"
......@@ -93,6 +84,7 @@ Sets
mstStart(mType, s, t) "Start point of samples"
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)"
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_new(f, t) "Newly introduced f,t to be used in calculating parameter/variable values"
......
......@@ -18,7 +18,8 @@ Scalars
* --- Power plant and fuel data -----------------------------------------------
Parameters
p_gn(grid, node, param_gn) "Data for energy nodes"
p_gn(grid, node, param_gn) "Properties for energy nodes"
p_gnBoundaryPropertiesForStates(grid, node, param_gnBoundaryTypes, param_gnBoundaryProperties) "Properties of different state boundaries and limits"
p_gnn(grid, node, node, param_gnn) "Data for interconnections between energy nodes"
p_gnu(grid, node, unit, param_gnu) "Unit data where energy type matters"
p_unit(unit, *) "Unit data where energy type does not matter"
......@@ -29,11 +30,6 @@ Parameters
p_effUnit(effSelector, unit, *) "Data for piece-wise linear efficiency blocks"
;
* --- Feasibility control -----------------------------------------------------
Parameters
p_gnSlack(inc_dec, slack, grid, node, param_slack) "Data for slack terms"
;
* --- Probability -------------------------------------------------------------
Parameters
p_sWeight(s) "Weight of sample"
......@@ -52,6 +48,7 @@ Parameters
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"
;
* --- Stochastic data parameters ----------------------------------------------
......@@ -67,7 +64,7 @@ Parameters
ts_reserveDemand(restype, resdirection, node, f, t) "Reserve demand in region in the time period/slice (MW)"
ts_reserveDemand_(restype, resdirection, node, f, t)
ts_nodeState(grid, node, param_gn, f, t) "Fix the states of a node according to time-series form exogenous input"
ts_nodeState(grid, node, param_gnBoundaryTypes, f, t) "Fix the states of a node according to time-series form exogenous input"
ts_fuelPriceChange(fuel, t) "Initial fuel price and consequent changes in fuel price (€/MWh)"
ts_fuelPriceChangenode(fuel, node, t) "Initial fuel price and consequent changes in fuel price in model nodegraphies (€/MWh)"
ts_unavailability(unit, t) "Unavailability of a unit in the time period/slice (p.u.)"
......
......@@ -16,7 +16,7 @@ $loaddc p_gnn
$loaddc p_gnu
$loaddc p_unit
$loaddc p_nuReserves
$loaddc p_gnSlack
$loaddc p_gnBoundaryPropertiesForStates
$loaddc p_gnPolicy
$loaddc p_uFuel
$loaddc flowUnit
......@@ -61,11 +61,13 @@ gn2n(grid, from_node, to_node)$p_gnn(grid, from_node, to_node, 'transferCap') =
node_to_node(from_node, to_node)$p_gnn('elec', from_node, to_node, 'transferCap') = yes;
gnn_boundState(grid, node, node_)$(p_gnn(grid, node, node_, 'boundStateOffset')) = yes;
gnn_state(grid, node, node_)$(p_gnn(grid, node, node_, 'diffCoeff') or gnn_boundState(grid, node, node_)) = yes;
gn_state(grid, node)$(p_gn(grid, node, 'maxState') or p_gn(grid, node, 'maxStateSlack') or sum(node_, gnn_state(grid, node, node_)) or sum(node_, gnn_state(grid, node_, node))) = yes;
gn_stateSlack(grid, node)$(p_gn(grid, node, 'maxStateSlack') and not p_gn(grid, node, 'fixTimeSeries') and not p_gn(grid, node, 'fixConstant')) = yes;
gn(grid, node)$(sum(unit, gnu(grid, node, unit)) or gn_state(grid, node)) = yes;
gnSlack(inc_dec, slack, grid, node)$(sum(param_slack, p_gnSlack(inc_dec, slack, grid, node, param_slack))) = yes;
p_gn(gn(grid, node), 'referenceMultiplier')$(not p_gn(grid, node, 'referenceMultiplier')) = 1; // If referenceMultiplier has not been set, set it to 1 by default
gn_stateSlack(grid, node)$(sum((upwardSlack, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, upwardSlack, useConstantOrTimeSeries))) = yes;
gn_stateSlack(grid, node)$(sum((downwardSlack, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, downwardSlack, useConstantOrTimeSeries))) = yes;
gn_state(grid, node)$gn_stateSlack(grid, node) = yes;
gn_state(grid, node)$(sum((stateLimits, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, stateLimits, useConstantOrTimeSeries))) = yes;
gn(grid, node)$(sum(unit, gnu(grid, node, unit) or gn_state(grid, node))) = yes;
p_gnBoundaryPropertiesForStates(gn(grid, node), param_gnBoundaryTypes, 'multiplier')$(not p_gnBoundaryPropertiesForStates(grid, node, param_gnBoundaryTypes, 'multiplier')) = 1; // If referenceMultiplier has not been set, set it to 1 by default
p_gn(gn(grid, node), 'unitConversion')$(not p_gn(grid, node, 'unitConversion')) = 1; // If unitConversion has not been set, default to 1
ts_fuelPriceChangenode(fuel, node, t) = ts_fuelPriceChange(fuel, t);
......@@ -83,7 +85,7 @@ unit_minload(unit)$[sum(gnu(grid, node, unit), p_gnu(grid, node, unit, 'rb00') >
*unit_hydro(unit)$sum(unitFuelParam(unit,'WATER','main'), 1) = yes;
*node_reservoir(node)$sum(unit_hydro, unitStorage(unit_hydro, storage)) = yes;
nuRescapable(restype, resdirection, node, unit)$p_nuReserves(node, unit, restype, resdirection) = yes;
node_spill(node)$(sum(grid, p_gn(grid, node, 'maxSpill'))) = yes;
node_spill(node)$(sum((grid, spillLimits, useConstantOrTimeSeries)$gn(grid, node), p_gnBoundaryPropertiesForStates(grid, node, spillLimits, useConstantOrTimeSeries))) = yes;
p_unit(unit, 'unitCount')$(not p_unit(unit, 'unitCount')) = 1; // In case number of units has not been defined it is 1.
$ontext
p_unit(unit, 'section00') = p_unit(unit, 'rb00') / p_unit(unit, 'eff00'); // Section at min. load defined by rb00
......
......@@ -10,7 +10,7 @@ SOS2 variable
Positive variables
v_fuelUse(fuel, unit, f, t) "Fuel use of a unit during time period (MWh_fuel)"
v_startup(unit, f, t) "Capacity started up from previous time period/slice (MW)"
v_state(grid, node, f, t) "State variable for nodes that maintain a state (MWh, unless modified by energyCapacity parameter)"
v_state(grid, node, f, t) "State variable for nodes that maintain a state (MWh, unless modified by unitConversion parameter)"
* v_stoCharge(grid, node, storage, f, t) "Charging of storage during time period (MWh)"
* v_stoDischarge(grid, node, storage, f, t) "Discharging of storage during time step (MWh)"
* v_stoContent(grid, node, storage, f, t) "Content of storage at the start of time period/time slice (MWh)"
......@@ -22,7 +22,7 @@ Positive variables
* --- Feasibility control -----------------------------------------------------
Positive variables
v_stateSlack(inc_dec, slack, grid, node, f, t) "Slack variable for different v_state slack categories, permits e.g. costs for exceeding acceptable v_states (MWh, unless modified by energyCapacity parameter)"
v_stateSlack(grid, node, slack, f, t) "Slack variable for different v_state slack categories, permits e.g. costs for exceeding acceptable v_states (MWh, unless modified by energyCapacity parameter)"
vq_gen(inc_dec, grid, node, f, t) "Dummy energy generation (increase) or consumption (generation decrease) to ensure equation feasibility (MW)"
vq_resDemand(restype, resdirection, node, f, t) "Dummy to decrease demand for a reserve (MW)"
;
......
......@@ -21,8 +21,9 @@ equations
* q_stoMinContent(grid, node, storage, f, t) "Storage should have enough content to discharge and to deliver committed upward reserves"
* q_stoMaxContent(grid, node, storage, f, t) "Storage should have enough room to fit scheduled charge and committed downward reserves"
q_transferLimit(grid, node, node, f, t) "Transfer of energy and capacity reservations are less than the transfer capacity"
q_maxStateSlack(grid, node, mType, f, t) "Slack variables keep track of state variables and connected reserves exceeding desired/permitted state limits"
q_minStateSlack(grid, node, mType, f, t) "Slack variables keep track of state variables and connected reserves under desired/permitted state limits"
q_stateSlack(grid, node, slack, f, t) "Slack variable greater than the difference between v_state and the slack boundary"
q_stateUpwardLimit(grid, node, f, t) "Limit the commitments of a node with a state variable to the available headrooms"
* q_stateDownwardLimit(grid, node, f, t) "Limit the commitments of a node with a state variable to the available headrooms"
q_boundState(grid, node, node, mType, f, t) "Node state variables bounded by other nodes"
;
......@@ -107,7 +108,7 @@ q_obj ..
// Dummy variables
+ sum(msft(m, s, f, t), p_sProbability(s) * p_fProbability(f) * (
sum(inc_dec,
sum( gn(grid, node)$(not p_gn(grid, node, 'fixState') and not p_gn(grid, node, 'fixTimeSeries')), vq_gen(inc_dec, grid, node, f, t) * (p_stepLength(m, f, t) + p_stepLength(m, f+pf(f,t), t+pt(t))${not p_stepLength(m, f, t)}) * PENALTY_BALANCE(grid) )
sum( gn(grid, node), vq_gen(inc_dec, grid, node, f, t) * (p_stepLength(m, f, t) + p_stepLength(m, f+pf(f,t), t+pt(t))${not p_stepLength(m, f, t)}) * PENALTY_BALANCE(grid) )
)
+ sum((restype, resdirection, node),
vq_resDemand(restype, resdirection, node, f, t)
......@@ -117,20 +118,25 @@ q_obj ..
)
)
// Node state slack variable penalties
+ sum(gnSlack(inc_dec, slack, grid, node)$gn_stateSlack(grid, node),
+ sum(gn_stateSlack(grid, node),
+ sum(msft(m, s, f, t),
+ p_sProbability(s) * p_fProbability(f) * ( p_gnSlack(inc_dec, slack, grid, node, 'costCoeff') * v_stateSlack(inc_dec, slack, grid, node, f, t) )
+ sum(upwardSlack,
+ p_sProbability(s) * p_fProbability(f) * ( p_gnBoundaryPropertiesForStates(grid, node, upwardSlack, 'slackCost') * v_stateSlack(grid, node, upwardSlack, f, t) )
)
+ sum(downwardSlack,
+ p_sProbability(s) * p_fProbability(f) * ( p_gnBoundaryPropertiesForStates(grid, node, downwardSlack, 'slackCost') * v_stateSlack(grid, node, downwardSlack, f, t) )
)
)
)
;
* -----------------------------------------------------------------------------
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, 'fixTimeSeries')) .. // Energy/power balance dynamics solved using implicit Euler discretization
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)
// The current state of the node
+ v_state(grid, node, f, t)$(gn_state(grid, node))
* ( // This multiplication transforms the state energy into power, a result of implicit discretization
+ p_gn(grid, node, 'energyCapacity') + 1$(not p_gn(grid, node, 'energyCapacity')) // Energy capacity assumed to be 1 if not given.
+ p_gn(grid, node, 'unitConversion') // Unit conversion factor has been assumed to be 1 if not given
+ (
+ p_gn(grid, node, 'selfDischargeLoss') // Self discharge rate
+ sum(node_$(gnn_state(grid, node_, node)), // Summation of the energy diffusion coefficients
......@@ -143,7 +149,7 @@ q_balance(gn(grid, node), m, ft_dynamic(f, t))$(p_stepLength(m, f+pf(f,t), t+pt(
// The previous state of the node
- v_state(grid, node, f+pf(f,t), t+pt(t))$(gn_state(grid, node))
* (
+ p_gn(grid, node, 'energyCapacity') + 1$(not p_gn(grid, node, 'energyCapacity')) // Energy capacity assumed to be 1 if not given.
+ p_gn(grid, node, 'unitConversion') // Unit conversion factor has been assumed to be 1 if not given.
$$ifi '%rampSched%' == 'yes' - ( p_gn(grid, node, 'selfDischargeLoss') + sum( node_$(gnn_state(grid, node_, node)), p_gnn(grid, node_, node, 'diffCoeff') ) * p_stepLength(m, f+pf(f,t), t+pt(t)) / 2 ) // Ramp scheduling averages the diffusion between timesteps
)
=E= // The right side of the equation contains all the changes converted to energy terms
......@@ -429,8 +435,44 @@ q_transferLimit(gn2n(grid, from_node, to_node), ft(f, t)) ..
+ p_gnn(grid, from_node, to_node, 'transferCap')
;
* -----------------------------------------------------------------------------
q_maxStateSlack(gn_state(grid, node), m, ft_dynamic(f, t))${ sum(slack, p_gnSlack('increase', slack, grid, node, 'maxSlack')) // Only if the node has a maxSlack parameter or
or ( v_state.up(grid, node, f, t) // node has an upward state limit
q_stateSlack(gn_stateSlack(grid, node), slack, ft(f, t)) ..
+ v_stateSlack(grid, node, slack, f, t) * p_slackDirection(slack)
=G=
+ v_state(grid, node, f, t)
- p_gnBoundaryPropertiesForStates(grid, node, slack, 'constant')$p_gnBoundaryPropertiesForStates(grid, node, slack, 'useConstant')
- ts_nodeState(grid, node, slack, f, t)$p_gnBoundaryPropertiesForStates(grid, node, slack, 'useTimeSeries')
;
q_stateUpwardLimit(gn_state(grid, node), ft(f, t))$( sum(nuResCapable(resType, resDirection, node, unit), nu(node, unit)) // If the node has units with reserves
or sum(nuResCapable(resType, resDirection, node, unit), gnu_input(grid, node, unit)) // or the node has a reserve capable input unit
) ..
+ v_state(grid, node, f, t)
+ p_stepLength(m, f, t) * (
+ (sum(nuRescapable(restype, 'resUp', node, unit),
+ v_reserve(resType, 'resUp', node, unit, f, t) // upward reserves from units that output energy to the node
)
+ sum(nuRescapable(restype, 'resDown', node_input, unit)${ sum(grid_, gnu_input(grid_, node_input, unit)) and gnu_output(grid, node, unit) }, // downward reserves from units that use the node as an input energy
+ v_reserve(restype, 'resDown', node_input, unit, f, t) // NOTE! If elec-elec conversion, this might result in weird reserve requirements!
* p_unit(unit, 'eff00') // NOTE! This is not correct, slope will change depending on the operating point. Maybe use maximum slope...
)
+ sum(gn2n(grid, from_node, node)${ sum(restypeDirection(restype, 'resUp'), restypeDirectionNode(restype, 'resUp', node)) },
+ sum(restype${ restypeDirectionNode(restype, 'resUp', node) },
+ (1 - p_gnn(grid, from_node, node, 'transferLoss')) * v_resTransfer(restype, 'resUp', from_node, node, f+pf(f,t), t+pt(t)) // Potential inflow from another node due to upward reserve import
)
)
+ sum(gn2n(grid, node, to_node)${ sum(restypeDirection(restype, resdirection), restypeDirectionNode(restype, resdirection, to_node)) },
+ sum(restype${ restypeDirectionNode(restype, 'resDown', to_node) },
+ (1 - p_gnn(grid, node, to_node, 'transferLoss')) * v_resTransfer(restype, 'resDown', node, to_node, f+pf(f,t), t+pt(t)) // Potential inflow from another node due to downward reserve export
)
)
)
=L=
+ p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'useConstant') * p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'constant')
+ p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'useTimeSeries') * ts_nodeState(grid, node, 'upwardLimit', f, t)
;
$ontext
q_maxStateSlack(gn_stateSlack(grid, node), m, ft_dynamic(f, t))${ v_state.up(grid, node, f, t) // node has an upward state limit
and ( // and the node is connected to units offering reserves
sum(nuRescapable(restype, resdirection, node, unit), nu(node, unit)) // Node has a reserve capable unit
or sum(nuRescapable(restype, resdirection, node_input, unit), gnu_input(grid, node_input, unit) + nu(node, unit)) // Node has a reserve capable input unit
......@@ -440,11 +482,10 @@ q_maxStateSlack(gn_state(grid, node), m, ft_dynamic(f, t))${ sum(slack, p_gnS
+ (
+ v_state(grid, node, f, t) // Node state
+ p_gn(grid, node, 'maxState')${not p_gn(grid, node, 'maxUseTimeSeries') and not p_gn(grid, node, 'maxStateSlack') and not ts_nodeState(grid, node, 'maxStateSlack', f, t)} // Constant maximum node state
+ ts_nodeState(grid, node, 'maxState', f, t)${p_gn(grid, node, maxUseTimeSeries) and not p_gn(grid, node, 'maxStateSlack') and not ts_nodeState(grid, node, 'maxStateSlack', f, t)} // Time series maximum node state
+ p_gnSlack('increase', slack, grid, node, 'maxSlack')
- v_state(grid, node, f, t) // Node state
+ sum(gnSlack('increase', slack, grid, node), // Summation over all determined slack categories
+ v_stateSlack('increase', slack, grid, node, f, t) // Downward slack variables
+ ts_nodeState(grid, node, 'maxState', f, t)${p_gn(grid, node, 'maxUseTimeSeries') and not p_gn(grid, node, 'maxStateSlack') and not ts_nodeState(grid, node, 'maxStateSlack', f, t)} // Time series maximum node state
+ sum(gn_stateSlack, grid, node), // Sum
+ p_gnSlack('increase', slack, grid, node, 'maxSlack')
+ v_stateSlack(grid, node, upwardSlack, f, t) // Upward slack variables
)
)
* ( p_gn(grid, node, 'energyCapacity') + 1$(not p_gn(grid, node, 'energyCapacity')) ) // Account for the possible energy capacity
......@@ -512,6 +553,7 @@ q_minStateSlack(gn_state(grid, node), m, ft(f, t))${ p_gn(grid, node, 'maxSta
)
* p_stepLength(m, f+pf(f,t), t+pt(t)) // Multiplication with the time step to get energy
;
$offtext
* -----------------------------------------------------------------------------
q_boundState(gnn_boundState(grid, node, node_), m, ft(f, t)) ..
+ v_state(grid, node, f, t) // The state of the first node sets the upper limit of the second
......@@ -534,7 +576,7 @@ q_boundState(gnn_boundState(grid, node, node_), m, ft(f, t)) ..
)
)
)
/ (p_gn(grid, node, 'energyCapacity') + 1${not p_gn(grid, node, 'energyCapacity')}) // Divide by the node energy capacity to obtain same unit as the state/time
/ (p_gn(grid, node, 'unitConversion') ) // Divide by the node energy capacity to obtain same unit as the state/time
* p_stepLength(m, f+pf(f,t), t+pt(t)) // Multiply with time step to obtain change in state over the step
=G=
+ v_state(grid, node_, f, t)
......@@ -558,7 +600,7 @@ q_boundState(gnn_boundState(grid, node, node_), m, ft(f, t)) ..
)
)
)
/ (p_gn(grid, node_, 'energyCapacity') + 1${not p_gn(grid, node_, 'energyCapacity')}) // Divide by the node energy capacity to obtain same unit as the state/time
/ (p_gn(grid, node_, 'unitConversion') ) // Divide by the node energy capacity to obtain same unit as the state/time
* p_stepLength(m, f+pf(f,t), t+pt(t)) // Multiply with time step to obtain change in state over the step
;
* --- Variable limits ---------------------------------------------------------
// v_state absolute boundaries set according to p_gn parameters;
// When using constant values and to supplement time series with constant values (time series will override when data available)
v_state.up(gn_state(grid, node), ft(f, t))$p_gn(grid, node, 'maxState') = p_gn(grid, node, 'maxState') * p_gn(grid, node, 'maxMultiplier');
v_state.up(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'maxState')) = p_gn(grid, node, 'maxState') * p_gn(grid, node, 'maxMultiplier');
v_state.lo(gn_state(grid, node), ft(f, t))$p_gn(grid, node, 'minState') = p_gn(grid, node, 'minState') * p_gn(grid, node, 'minMultiplier');
v_state.lo(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'minState')) = p_gn(grid, node, 'minState') * p_gn(grid, node, 'minMultiplier');
v_state.fx(gn_state(grid, node), ft(f, t))$(p_gn(grid, node, 'fixConstant')) = p_gn(grid, node, 'referenceState') * p_gn(grid, node, 'referenceMultiplier');
v_state.fx(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'fixConstant')) = p_gn(grid, node, 'referenceState') * p_gn(grid, node, 'referenceMultiplier');
v_state.up(gn_state(grid, node), ft_full(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'useConstant') = p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'multiplier');
v_state.lo(gn_state(grid, node), ft_full(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'useConstant') = p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'multiplier');
v_state.fx(gn_state(grid, node), ft_full(f, t))$(p_gn(grid, node, 'boundAll') and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useConstant')) = p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
// When using time series
v_state.up(gn_state(grid, node), ft(f, t))$p_gn(grid, node, 'maxUseTimeSeries') = ts_nodeState(grid, node, 'maxState', f, t) * p_gn(grid, node, 'maxMultiplier');
v_state.up(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'maxUseTimeSeries')) = ts_nodeState(grid, node, 'maxState', f, t) * p_gn(grid, node, 'maxMultiplier');
v_state.lo(gn_state(grid, node), ft(f, t))$p_gn(grid, node, 'minUseTimeSeries') = ts_nodeState(grid, node, 'minState', f, t) * p_gn(grid, node, 'minMultiplier');
v_state.lo(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'minUseTimeSeries')) = ts_nodeState(grid, node, 'minState', f, t) * p_gn(grid, node, 'minMultiplier');
v_state.fx(gn_state(grid, node), ft(f, t))$(p_gn(grid, node, 'fixTimeSeries')) = p_gn(grid, node, 'referenceState') * p_gn(grid, node, 'referenceMultiplier');
v_state.fx(gn_state(grid, node), f, t)$(mftLastSteps(mSolve, f, t) and p_gn(grid, node, 'fixTimeSeries')) = p_gn(grid, node, 'referenceState') * p_gn(grid, node, 'referenceMultiplier');
v_state.up(gn_state(grid, node), ft_full(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'useTimeSeries') = ts_nodeState(grid, node, 'upwardLimit', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'upwardLimit', 'multiplier');
v_state.lo(gn_state(grid, node), ft_full(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'useTimeSeries') = ts_nodeState(grid, node, 'downwardLimit', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'multiplier');
v_state.fx(gn_state(grid, node), ft_full(f, t))$(p_gn(grid, node, 'boundAll') and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useTimeSeries')) = ts_nodeState(grid, node, 'reference', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
* Fix state variables for the first solve if required
* Bound state variables for the first solve if required
loop(ft(f, tSolve),
// First solve, state variables (only if fixStart flag is true)
v_state.fx(grid, node, f, tSolve)$(gn_state(grid,node) and p_gn(grid, node, 'fixStart') and tSolveFirst = mSettings('schedule', 't_start'))
= + p_gn(grid, node, 'fixConstant') * p_gn(grid, node, 'referenceState') * p_gn(grid, node, 'referenceMultiplier')
+ p_gn(grid, node, 'fixTimeSeries') * ts_nodeState(grid, node, 'fixTimeSeries', f, tSolve) * p_gn(grid, node, 'referenceMultiplier');
// Remaining solves once v_state has been established (only if fixStart flag is true)
v_state.fx(grid, node, f, tSolve)$(gn_state(grid,node) and p_gn(grid, node, 'fixStart') and not tSolveFirst = mSettings('schedule', 't_start'))
// First solve, state variables (only if boundStart flag is true)
v_state.fx(grid, node, f, tSolve)$(gn_state(grid,node) and p_gn(grid, node, 'boundStart') and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useConstant') and tSolveFirst = mSettings('schedule', 't_start'))
= p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
v_state.fx(grid, node, f, tSolve)$(gn_state(grid,node) and p_gn(grid, node, 'boundStart') and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useTimeSeries') and tSolveFirst = mSettings('schedule', 't_start'))
= ts_nodeState(grid, node, 'reference', f, tSolve) * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
// Remaining solves will use bound start value for v_state once it has been established
v_state.fx(grid, node, f, tSolve)$(gn_state(grid,node) and not tSolveFirst = mSettings('schedule', 't_start'))
= v_state.l(grid, node, f, tSolve);
// First solve, online variables
v_online.up(uft(unit, f, tSolve))$(tSolveFirst = mSettings('schedule', 't_start')) = p_unit(unit, 'unitCount');
// Remaining solves
v_online.fx(uft(unit, f, tSolve))$(not tSolveFirst = mSettings('schedule', 't_start')) = round(v_online.l(unit, f, tSolve));
);
);
// v_stateSlack absolute boundaries determined by the slack data in p_gnSlack
v_stateSlack.up(gnSlack(inc_dec, slack, grid, node), ft(f, t))$p_gnSlack(inc_dec, slack, grid, node, 'maxSlack') = p_gnSlack(inc_dec, slack, grid, node, 'maxSlack');
v_stateSlack.up(gnSlack(inc_dec, slack, grid, node), f, t)${mftLastSteps(mSolve, f, t) and p_gnSlack(inc_dec, slack, grid, node, 'maxSlack')} = p_gnSlack(inc_dec, slack, grid, node, 'maxSlack');
*v_stateSlack.up(gn_stateSlack(grid, node), slack, ft_full(f, t))$param_gnBoundaryTypes(grid, node, slack, 'useConstant') = p_gnSlack(grid, node, slack, 'constant') * param_gnBoundaryTypes(grid, node, slack, 'multiplier');
*v_stateSlack.up(gn_stateSlack(grid, node), slack, ft_full(f, t))$param_gnBoundaryTypes(grid, node, slack, 'useTimeSeries') = ts_nodeState(grid, node, slack, f, t) * param_gnBoundaryTypes(grid, node, slack, 'multiplier');
* Other time dependent parameters and variable limits
// Max. energy generation
......@@ -59,7 +53,7 @@ v_gen.lo(gnuft(grid, node, unit, f, t))$gnu_input(grid, node, unit) = -p_gnu(gri
// v_online cannot exceed unit count
v_online.up(uft(unit, f, t))$sum(effSelector$(not effDirectOff(effSelector)), suft(effSelector, unit, f, t)) = p_unit(unit, 'unitCount');
// Restrict v_online also in the last dynamic time step
v_online.up(uft(unit, f, t))$(sum(effSelector$(not effDirectOff(effSelector)), suft(effSelector, unit, f, t)) and mftLastSteps(mSolve,f,t)) = p_unit(unit, 'unitCount');
v_online.up(unit, f, t)$(sum[effSelector$(not effDirectOff(effSelector)), suft(effSelector, unit, f, t)] and mftLastSteps(mSolve, f, t)) = p_unit(unit, 'unitCount');
// Free storage control ...
......@@ -74,10 +68,14 @@ v_online.up(uft(unit, f, t))$(sum(effSelector$(not effDirectOff(effSelector)), s
* );
// Max. & min. spilling
v_spill.lo(gn(grid, node), ft(f, t))$p_gn(grid, node, 'minSpill')
= p_gn(grid, node, 'minSpill') * p_stepLength(mSolve, f, t);
v_spill.up(gn(grid, node), ft(f, t))$p_gn(grid, node, 'maxSpill')
= p_gn(grid, node, 'maxSpill') * p_stepLength(mSolve, f, t);
v_spill.lo(gn(grid, node), ft(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'useConstant')
= p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'multiplier') * p_stepLength(mSolve, f, t);
v_spill.lo(gn(grid, node), ft(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'useTimeSeries')
= ts_nodeState(grid, node, 'minSpill', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'multiplier') * p_stepLength(mSolve, f, t);
v_spill.up(gn(grid, node), ft(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'useConstant')
= p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'multiplier') * p_stepLength(mSolve, f, t);
v_spill.up(gn(grid, node), ft(f, t))$p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'useTimeSeries')
= ts_nodeState(grid, node, 'maxSpill', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'multiplier') * p_stepLength(mSolve, f, t);
// Restrictions on transferring energy between nodes
v_transfer.up(gn2n(grid, from_node, to_node), ft(f, t))
......
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