Commit 160ea18f authored by Niina Helistö's avatar Niina Helistö
Browse files

Corrections to ramping constraints and testing different start-up types (hot,...

Corrections to ramping constraints and testing different start-up types (hot, warm, cold) and minimum up/down times.
parent e57eea9e
......@@ -76,6 +76,9 @@ Model invest /
q_onlineLimit
q_rampUpLimit
q_rampDownLimit
q_startuptype
q_minUp
q_minDown
q_capacityMargin
q_emissionCap
/;
......@@ -67,6 +67,8 @@ Sets // Model related selections
/ up, down /
inc_dec "Increase or decrease in dummy, or slack variables"
/ increase, decrease /
min_max "Minimum and maximum"
/ min, max /
;
Sets //Reserve type sets
......@@ -85,6 +87,16 @@ Sets //Reserve type sets
/
;
Sets //Startup type sets
starttype "Startup types"
/ hot "Hot start"
warm "Warm start"
cold "Cold start"
/
starttypeConstrained(starttype) "Startup types with constrained maximum non-opearational time"
/ hot, warm /
;
* Numeric parameters
Parameter
settings(mSetting)
......
......@@ -54,6 +54,7 @@ Parameters
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)"
p_gnugnu(grid, node, unit, grid, node, unit, param_gnugnu) "Data connecting units in nodes and grids"
p_uNonoperational(unit, starttype, min_max) "Non-operational time after being shut down before start up"
// Time dependent unit & fuel parameters
ts_unit(unit, *, f, t) "Time dependent unit data, where energy type doesn't matter"
ts_effUnit(effSelector, unit, effSelector, *, f, t) "Time dependent data for piece-wise linear efficiency blocks"
......
......@@ -111,6 +111,10 @@ p_unitFuelEmissionCost(unit_fuel, fuel, emission)$sum(param_fuel, uFuel(unit_fue
/ sum(gnu_output(grid, node, unit_fuel), p_gnu(grid, node, unit_fuel, 'maxGen')
+ p_gnu(grid, node, unit_fuel, 'unitSizeGen')$(not p_gnu(grid, node, unit_fuel, 'maxGen'))
);
p_uNonoperational(unit, 'hot', 'min') = 0;
p_uNonoperational(unit, 'hot', 'max') = p_unit(unit, 'startWarm');
p_uNonoperational(unit, 'warm', 'min') = p_unit(unit, 'startWarm');
p_uNonoperational(unit, 'warm', 'max') = p_unit(unit, 'startCold');
p_gnu(grid, node, unit, 'unitSizeGen')$(p_gnu(grid, node, unit, 'maxGen') and p_unit(unit, 'unitCount')) = p_gnu(grid, node, unit, 'maxGen')/p_unit(unit, 'unitCount'); // If maxGen and unitCount are given, calculate unitSizeGen based on them.
p_gnu(grid, node, unit, 'unitSizeCons')$(p_gnu(grid, node, unit, 'maxCons') and p_unit(unit, 'unitCount')) = p_gnu(grid, node, unit, 'maxCons')/p_unit(unit, 'unitCount'); // If maxCons and unitCount are given, calculate unitSizeCons based on them.
* Generate node related sets based on input data // NOTE! These will need to change if p_gnn is required to work with only one row per link.
......
......@@ -31,7 +31,7 @@ SOS2 variables
;
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 after/during the time period/slice (MW)"
v_startup(unit, starttype, f, t) "Capacity started up after/during the time period/slice (MW)"
v_shutdown(unit, f, t) "Capacity shut down after/during the time period/slice (MW)"
v_genRampChange(grid, node, unit, up_down, f, t) "Rate of change in energy generation between time steps (MW/h)"
v_spill(grid, node, f, t) "Spill of energy from storage node during time period (MWh)"
......
......@@ -44,6 +44,9 @@ equations
q_onlineLimit(unit, f, t) "Number of online units limited for units with investment possibility"
q_rampUpLimit(grid, node, mType, unit, f, t) "Up ramping limited for units"
q_rampDownLimit(grid, node, mType, unit, f, t) "Down ramping limited for units"
q_startuptype(mType, unit, starttype, f, t) "Startup type depends on the time the unit has been non-operational"
q_minUp(mType, unit, f, t) "Unit must stay operational if it has started up during the previous minOperationTime hours"
q_minDown(mType, unit, f, t) "Unit must stay non-operational if it has shut down during the previous minShutDownTime hours"
q_capacityMargin(grid, node, f, t) "There needs to be enough capacity to cover energy demand plus a margin"
q_emissioncap(grid, node, emission) "Limit for emissions"
;
......@@ -86,7 +89,7 @@ q_obj ..
// Start-up costs
+ sum(uft_online(unit, f, t),
+ {
+ v_startup(unit, f, t) // Cost of starting up
+ v_startup(unit, 'hot', f, t) // Cost of starting up
}
* {
// Startup variable costs
......@@ -105,6 +108,46 @@ q_obj ..
)
)
}
+ {
+ v_startup(unit, 'warm', f, t) // Cost of starting up
}
* {
// Startup variable costs
+ p_unit(unit, 'startCostWarm')${not unit_investLP(unit)}
+ p_unit(unit, 'startCostWarm_MW')$unit_investLP(unit)
// Start-up fuel and emission costs
+ sum(uFuel(unit, 'startup', fuel)$unit_fuel(unit),
(
+ p_unit(unit, 'startFuelConsWarm')${not unit_investLP(unit)}
+ p_unit(unit, 'startFuelConsWarm_MW')$unit_investLP(unit)
)
* ( + sum{tFuel$[ord(tFuel) <= ord(t)],
ts_fuelPriceChange(fuel, tFuel) } // Fuel costs for start-up fuel use
+ sum(emission, // Emission taxes of startup fuel use
p_unitFuelEmissionCost(unit, fuel, emission) )
)
)
}
+ {
+ v_startup(unit, 'cold', f, t) // Cost of starting up
}
* {
// Startup variable costs
+ p_unit(unit, 'startCostCold')${not unit_investLP(unit)}
+ p_unit(unit, 'startCostCold_MW')$unit_investLP(unit)
// Start-up fuel and emission costs
+ sum(uFuel(unit, 'startup', fuel)$unit_fuel(unit),
(
+ p_unit(unit, 'startFuelConsCold')${not unit_investLP(unit)}
+ p_unit(unit, 'startFuelConsCold_MW')$unit_investLP(unit)
)
* ( + sum{tFuel$[ord(tFuel) <= ord(t)],
ts_fuelPriceChange(fuel, tFuel) } // Fuel costs for start-up fuel use
+ sum(emission, // Emission taxes of startup fuel use
p_unitFuelEmissionCost(unit, fuel, emission) )
)
)
}
)
// Ramping costs
+ sum(gnuft_ramp(grid, node, unit, f, t)${ p_gnu(grid, node, unit, 'rampUpCost') OR p_gnu(grid, node, unit, 'rampDownCost') },
......@@ -409,7 +452,7 @@ q_startup(unit, ft_dynamic(f, t))${ uft_online(unit, f, t)
=E=
+ v_online(unit, f+pf(f,t), t+pt(t)) // This reaches to tFirstSolve when pt = -1
+ v_online_LP(unit, f+pf(f,t), t+pt(t))
+ v_startup(unit, f, t+pt(t))
+ sum(starttype, v_startup(unit, starttype, f, t+pt(t)))
- v_shutdown(unit, f, t+pt(t))
;
* -----------------------------------------------------------------------------
......@@ -720,7 +763,7 @@ q_rampUpLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, node,
=L=
// Ramping capability of units that were online both in the previous time step and the current time step
+ (
+ 1${not uft_online(unit, f+pf(f,t), t+pt(t))} // Taking into account units without online variable
+ p_unit(unit, 'unitCount')${not uft_online(unit, f+pf(f,t), t+pt(t))} // Taking into account units without online variable
+ v_online_LP(unit, f+pf(f,t), t+pt(t))${uft_online(unit, f+pf(f,t), t+pt(t))} // Units with continuous online variable?
+ v_online(unit, f+pf(f,t), t+pt(t))${uft_online(unit, f+pf(f,t), t+pt(t))}
- v_shutdown(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
......@@ -733,11 +776,11 @@ q_rampUpLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, node,
+ sum(gnu(grid_, node_, unit), p_gnu(grid_, node_, unit, 'unitSizeGen') + p_gnu(grid_, node_, unit, 'unitSizeCons'))${
unit_investLP(unit) and ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
}
}
} // Scaling factor to calculate online capacity in gn(grid, node) in the case of continuous investments
// Newly started units are assumed to start to their minload and
// newly shutdown units are assumed to be shut down from their minload.
+ (
+ v_startup(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
+ sum(starttype, v_startup(unit, starttype, f, t+pt(t))${uft_online(unit, f, t+pt(t))})
- v_shutdown(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
)
* ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
......@@ -746,8 +789,9 @@ q_rampUpLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, node,
+ sum(gnu(grid_, node_, unit), p_gnu(grid_, node_, unit, 'unitSizeGen') + p_gnu(grid_, node_, unit, 'unitSizeCons'))${
unit_investLP(unit) and ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
}
}
} // Scaling factor to calculate online capacity in gn(grid, node) in the case of continuous investments
* sum(suft(effGroup, unit, f+cf(f,t), t), p_effGroupUnit(effGroup, unit, 'lb'))
// Reserve provision?
;
* -----------------------------------------------------------------------------
q_rampDownLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, node, unit, f, t)} ..
......@@ -756,7 +800,7 @@ q_rampDownLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, nod
=G=
// Ramping capability of units that were online both in the previous time step and the current time step
- (
+ 1${not uft_online(unit, f+pf(f,t), t+pt(t))} // Taking into account units without online variable
+ p_unit(unit, 'unitCount')${not uft_online(unit, f+pf(f,t), t+pt(t))} // Taking into account units without online variable
+ v_online_LP(unit, f+pf(f,t), t+pt(t))${uft_online(unit, f+pf(f,t), t+pt(t))} // Units with continuous online variable?
+ v_online(unit, f+pf(f,t), t+pt(t))${uft_online(unit, f+pf(f,t), t+pt(t))}
- v_shutdown(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
......@@ -769,11 +813,11 @@ q_rampDownLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, nod
+ sum(gnu(grid_, node_, unit), p_gnu(grid_, node_, unit, 'unitSizeGen') + p_gnu(grid_, node_, unit, 'unitSizeCons'))${
unit_investLP(unit) and ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
}
}
} // Scaling factor to calculate online capacity in gn(grid, node) in the case of continuous investments
// Newly started units are assumed to start to their minload and
// newly shutdown units are assumed to be shut down from their minload.
+ (
+ v_startup(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
+ sum(starttype, v_startup(unit, starttype, f, t+pt(t))${uft_online(unit, f, t+pt(t))})
- v_shutdown(unit, f, t+pt(t))${uft_online(unit, f, t+pt(t))}
)
* ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
......@@ -782,8 +826,43 @@ q_rampDownLimit(gn(grid, node), m, unit, ft_dynamic(f, t))${gnuft_ramp(grid, nod
+ sum(gnu(grid_, node_, unit), p_gnu(grid_, node_, unit, 'unitSizeGen') + p_gnu(grid_, node_, unit, 'unitSizeCons'))${
unit_investLP(unit) and ( p_gnu(grid, node, unit, 'unitSizeGen') - p_gnu(grid, node, unit, 'unitSizeCons') )
}
}
} // Scaling factor to calculate online capacity in gn(grid, node) in the case of continuous investments
* sum(suft(effGroup, unit, f+cf(f,t), t), p_effGroupUnit(effGroup, unit, 'lb'))
// Reserve provision?
;
*-----------------------------------------------------------------------------
q_startuptype(m, unit, starttype, f, t)${ uft_online(unit, f, t)
and ft_dynamic(f,t)
and starttypeConstrained(starttype)} ..
+ v_startup(unit, starttype, f, t)
=L=
+ sum(t_${ ord(t_)>[ord(t)-p_uNonoperational(unit, starttype, 'max') / mSettings(m, 'intervalInHours')]
and ord(t_)<=[ord(t)-p_uNonoperational(unit, starttype, 'min') / mSettings(m, 'intervalInHours')]
}, v_shutdown(unit, f, t_)
)
* How to take into account varying time step lengths? And forecasts?
;
*-----------------------------------------------------------------------------
q_minUp(m, unit, f, t)${uft_online(unit, f, t) and ft_dynamic(f,t) and not unit_investLP(unit)} ..
+ sum(t_${ ord(t_)>=[ord(t)-p_unit(unit, 'minOperationTime') / mSettings(m, 'intervalInHours')]
and ord(t_)<ord(t)
}, sum(starttype, v_startup(unit, starttype, f, t_))
)
=L=
+ v_online(unit, f, t)
* How to take into account varying time step lengths? And forecasts?
;
*-----------------------------------------------------------------------------
q_minDown(m, unit, f, t)${uft_online(unit, f, t) and ft_dynamic(f,t) and not unit_investLP(unit)} ..
+ sum(t_${ ord(t_)>=[ord(t)-p_unit(unit, 'minShutDownTime') / mSettings(m, 'intervalInHours')]
and ord(t_)<ord(t)
}, v_shutdown(unit, f, t_)
)
=L=
+ p_unit(unit, 'unitCount')
+ sum(t__$(ord(t__)<=ord(t)), v_invest_MIP(unit, t__))
- v_online(unit, f, t)
* How to take into account varying time step lengths? And forecasts?
;
*-----------------------------------------------------------------------------
q_capacityMargin(gn(grid, node), ft(f, t))${p_gn(grid, node, 'capacityMargin')} ..
......@@ -836,7 +915,7 @@ q_emissioncap(grid, node, emission)${p_gnPolicy(grid, node, 'emissionCap', emiss
// Start-up emissions
+ sum(uft_online(unit, f, t),
+ {
+ v_startup(unit, f, t)
+ v_startup(unit, 'hot', f, t)
}
* {
+ sum(uFuel(unit, 'startup', fuel)$unit_fuel(unit),
......@@ -855,6 +934,47 @@ q_emissioncap(grid, node, emission)${p_gnPolicy(grid, node, 'emissionCap', emiss
)
)
}
+ {
+ v_startup(unit, 'warm', f, t)
}
* {
+ sum(uFuel(unit, 'startup', fuel)$unit_fuel(unit),
(
+ p_unit(unit, 'startFuelConsWarm')${not unit_investLP(unit)}
+ p_unit(unit, 'startFuelConsWarm_MW')$unit_investLP(unit)
)
* (
p_fuelEmission(fuel, emission) / 1e3
* (p_gnu(grid, node, unit, 'maxGen')
+ p_gnu(grid, node, unit, 'unitSizeGen')$(not p_gnu(grid, node, unit, 'maxGen'))
) // Weighted emissions from different output energy types
/ sum(gnu_output(grid_, node_, unit), p_gnu(grid_, node_, unit, 'maxGen')
+ p_gnu(grid_, node_, unit, 'unitSizeGen')$(not p_gnu(grid_, node_, unit, 'maxGen'))
)
)
)
}
+ {
+ v_startup(unit, 'cold', f, t)
}
* {
+ sum(uFuel(unit, 'startup', fuel)$unit_fuel(unit),
(
+ p_unit(unit, 'startFuelConsCold')${not unit_investLP(unit)}
+ p_unit(unit, 'startFuelConsCold_MW')$unit_investLP(unit)
)
* (
p_fuelEmission(fuel, emission) / 1e3
* (p_gnu(grid, node, unit, 'maxGen')
+ p_gnu(grid, node, unit, 'unitSizeGen')$(not p_gnu(grid, node, unit, 'maxGen'))
) // Weighted emissions from different output energy types
/ sum(gnu_output(grid_, node_, unit), p_gnu(grid_, node_, unit, 'maxGen')
+ p_gnu(grid_, node_, unit, 'unitSizeGen')$(not p_gnu(grid_, node_, unit, 'maxGen'))
)
)
)
}
)
)
)
......
......@@ -151,6 +151,12 @@ v_investTransfer_MIP.up(gn2n(grid, from_node, to_node), t)${p_gnn(grid, from_nod
v_investTransfer_MIP.fx(gn2n(grid, from_node, to_node), t)${not p_gnn(grid, from_node, to_node, 'investMIP')
or not t_invest(t)
} = 0;
// If offline hours after which the start-up will be a warm/cold start is not
// defined, fix hot/warm start-up to zero.
v_startup.fx(unit, 'hot', ft_dynamic(f, t))${not p_unit(unit, 'startWarm')} = 0;
v_startup.fx(unit, 'warm', ft_dynamic(f, t))${not p_unit(unit, 'startCold')} = 0;
// Fix reserves between t_jump and gate_closure based on previous allocations
loop(restypeDirectionNode(restypeDirection(restype, up_down), node),
* if ([not mod(tSolveFirst-1, p_nReserves(node, restype, 'update_frequency')) and not tSolveFirst = mSettings(mSolve, 't_start')],
......
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