Module nemo.scenarios
Supply side scenarios.
Expand source code
# Copyright (C) 2012, 2013, 2014, 2022 Ben Elliston
# Copyright (C) 2014, 2015, 2016 The University of New South Wales
# Copyright (C) 2016 IT Power (Australia)
# Copyright (C) 2023 Ben Elliston
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
"""Supply side scenarios."""
from nemo import configfile, regions
from nemo.generators import (CCGT, CCGT_CCS, CST, OCGT, Biofuel, Black_Coal,
CentralReceiver, Coal_CCS, DemandResponse, Hydro,
PumpedHydroPump, PumpedHydroTurbine, PV1Axis,
Wind, WindOffshore)
from nemo.polygons import (WILDCARD, cst_limit, offshore_wind_limit, pv_limit,
wind_limit)
from nemo.storage import PumpedHydroStorage
from nemo.types import UnreachableError
def _demand_response():
"""Return a list of DR 'generators'."""
dr1 = DemandResponse(WILDCARD, 1000, 100, "DR100")
dr2 = DemandResponse(WILDCARD, 1000, 500, "DR500")
dr3 = DemandResponse(WILDCARD, 1000, 1000, "DR1000")
return [dr1, dr2, dr3]
def _pumped_hydro():
"""Return a list of existing pumped hydro generators."""
# QLD: Wivenhoe (http://www.csenergy.com.au/content-%28168%29-wivenhoe.htm)
psh17stg = PumpedHydroStorage(5000, label='poly 17 pumped storage')
psh17pump = PumpedHydroPump(17, 500, psh17stg, label='poly 17 PSH pump')
psh17turb = PumpedHydroTurbine(17, 500, psh17stg,
label='poly 17 PSH generator')
# NSW: Tumut 3 (6x250), Bendeela (2x80) and Kangaroo Valley (2x40)
psh36stg = PumpedHydroStorage(15000, label='Tumut 3 storage')
psh36pump = PumpedHydroPump(36, 1740, psh36stg, label='Tumut 3 pump')
psh36turb = PumpedHydroTurbine(36, 1740, psh36stg,
label='Tumut 3 generator')
return [psh17pump, psh36pump, psh17turb, psh36turb]
def _hydro():
"""Return a list of existing hydroelectric generators."""
hydro24 = Hydro(24, 42.5, label='poly 24 hydro')
hydro31 = Hydro(31, 43, label='poly 31 hydro')
hydro35 = Hydro(35, 71, label='poly 35 hydro')
hydro36 = Hydro(36, 2513.9, label='poly 36 hydro')
hydro38 = Hydro(38, 450, label='poly 38 hydro')
hydro39 = Hydro(39, 13.8, label='poly 39 hydro')
hydro40 = Hydro(40, 586.6, label='poly 40 hydro')
hydro41 = Hydro(41, 280, label='poly 41 hydro')
hydro42 = Hydro(42, 590.4, label='poly 42 hydro')
hydro43 = Hydro(43, 462.5, label='poly 43 hydro')
return [hydro24, hydro31, hydro35, hydro36, hydro38, hydro39,
hydro40, hydro41, hydro42, hydro43]
def replacement(context):
"""Replace the current NEM fleet, more or less."""
context.generators = \
[Black_Coal(WILDCARD, 0)] + _pumped_hydro() + _hydro() + \
[OCGT(WILDCARD, 0)]
def _one_ccgt(context):
"""One CCGT only."""
context.generators = [CCGT(WILDCARD, 0)]
def ccgt(context):
"""All gas scenario."""
context.generators = [CCGT(WILDCARD, 0)] + _pumped_hydro() + \
_hydro() + [OCGT(WILDCARD, 0)]
def ccgt_ccs(context):
"""CCGT CCS scenario."""
# pylint: disable=redefined-outer-name
ccgt = CCGT_CCS(WILDCARD, 0)
ocgt = OCGT(WILDCARD, 0)
context.generators = [ccgt] + _pumped_hydro() + _hydro() + [ocgt]
def coal_ccs(context):
"""Coal CCS scenario."""
coal = Coal_CCS(WILDCARD, 0)
ocgt = OCGT(WILDCARD, 0)
context.generators = [coal] + _pumped_hydro() + _hydro() + [ocgt]
def _every_poly(gentype):
"""Create a generator of type gentype in each of the 44 polygons."""
result = []
for poly in range(1, 44):
if gentype == Biofuel:
result.append(gentype(poly, 0, label=f'polygon {poly} GT'))
elif gentype == PV1Axis:
cfg = configfile.get('generation', 'pv1axis-trace')
result.append(gentype(poly, 0, cfg, poly - 1,
build_limit=pv_limit[poly],
label=f'polygon {poly} PV'))
elif gentype == CentralReceiver:
cfg = configfile.get('generation', 'cst-trace')
result.append(gentype(poly, 0, 2.0, 6, cfg, poly - 1,
build_limit=cst_limit[poly],
label=f'polygon {poly} CST'))
elif gentype == Wind:
cfg = configfile.get('generation', 'wind-trace')
result.append(gentype(poly, 0, cfg, poly - 1,
build_limit=wind_limit[poly],
label=f'polygon {poly} wind'))
return result
def re100(context):
"""100% renewable electricity."""
result = []
# The following list is in merit order.
for g in [PV1Axis, Wind, WindOffshore, PumpedHydroTurbine, Hydro,
CentralReceiver, Biofuel]:
if g == PumpedHydroTurbine:
result += _pumped_hydro()
elif g == Hydro:
result += _hydro()
elif g == WindOffshore:
cfg = configfile.get('generation', 'offshore-wind-trace')
for column, poly in enumerate([31, 36, 38, 40]):
result.append(g(poly, 0, cfg, column,
build_limit=offshore_wind_limit[poly],
label='polygon {poly} offshore'))
elif g in [Biofuel, PV1Axis, CentralReceiver, Wind]:
result += _every_poly(g)
else:
raise UnreachableError('unhandled generator type')
context.generators = result
def _one_per_poly(region):
"""Return three lists of wind, PV and CST generators, one per polygon."""
pv = []
wind = []
cst = []
wind_cfg = configfile.get('generation', 'wind-trace')
pv_cfg = configfile.get('generation', 'pv1axis-trace')
cst_cfg = configfile.get('generation', 'cst-trace')
for poly in region.polygons:
wind.append(Wind(poly, 0, wind_cfg,
poly - 1,
build_limit=wind_limit[poly],
label=f'poly {poly} wind'))
pv.append(PV1Axis(poly, 0, pv_cfg,
poly - 1,
build_limit=pv_limit[poly],
label=f'poly {poly} PV'))
cst.append(CentralReceiver(poly, 0, 2.5, 8, cst_cfg,
poly - 1,
build_limit=cst_limit[poly],
label=f'poly {poly} CST'))
return wind, pv, cst
def re100_one_region(context, region):
"""100% renewables in one region only."""
re100(context)
context.regions = [region]
wind, pv, cst = _one_per_poly(region)
newlist = wind
newlist += pv
newlist += [g for g in context.generators if
isinstance(g, Hydro) and g.region() is region]
newlist += cst
newlist += [g for g in context.generators if
isinstance(g, Biofuel) and g.region() is region]
context.generators = newlist
def re_plus_ccs(context):
"""Mostly renewables with fossil and CCS augmentation."""
re100(context)
coal = Black_Coal(WILDCARD, 0)
# pylint: disable=redefined-outer-name
coal_ccs = Coal_CCS(WILDCARD, 0)
# pylint: disable=redefined-outer-name
ccgt = CCGT(WILDCARD, 0)
ccgt_ccs = CCGT_CCS(WILDCARD, 0)
ocgt = OCGT(WILDCARD, 0)
context.generators = [coal, coal_ccs, ccgt, ccgt_ccs] + \
context.generators[:-4] + [ocgt]
def re_plus_fossil(context):
"""Mostly renewables with some fossil augmentation."""
re100(context)
context.generators = \
[Black_Coal(WILDCARD, 0), CCGT(WILDCARD, 0)] + \
context.generators[:-4] + [OCGT(WILDCARD, 0)]
def re100_dsp(context):
"""Mostly renewables with demand side participation."""
re100(context)
context.generators += _demand_response()
def re100_nocst(context):
"""100% renewables, but no CST."""
re100(context)
newlist = [g for g in context.generators if not isinstance(g, CST)]
context.generators = newlist
def re100_nsw(context):
"""100% renewables in New South Wales only."""
re100_one_region(context, regions.nsw)
def re100_qld(context):
"""100% renewables in Queensland only."""
re100_one_region(context, regions.qld)
def re100_south_aus(context):
"""100% renewables in South Australia only."""
re100_one_region(context, regions.sa)
supply_scenarios = {'__one_ccgt__': _one_ccgt, # nb. for testing only
'ccgt': ccgt,
'ccgt-ccs': ccgt_ccs,
'coal-ccs': coal_ccs,
're+ccs': re_plus_ccs,
're+fossil': re_plus_fossil,
're100': re100,
're100-qld': re100_qld,
're100-nsw': re100_nsw,
're100-sa': re100_south_aus,
're100+dsp': re100_dsp,
're100-nocst': re100_nocst,
'replacement': replacement}
Functions
def ccgt(context)
-
All gas scenario.
Expand source code
def ccgt(context): """All gas scenario.""" context.generators = [CCGT(WILDCARD, 0)] + _pumped_hydro() + \ _hydro() + [OCGT(WILDCARD, 0)]
def ccgt_ccs(context)
-
CCGT CCS scenario.
Expand source code
def ccgt_ccs(context): """CCGT CCS scenario.""" # pylint: disable=redefined-outer-name ccgt = CCGT_CCS(WILDCARD, 0) ocgt = OCGT(WILDCARD, 0) context.generators = [ccgt] + _pumped_hydro() + _hydro() + [ocgt]
def coal_ccs(context)
-
Coal CCS scenario.
Expand source code
def coal_ccs(context): """Coal CCS scenario.""" coal = Coal_CCS(WILDCARD, 0) ocgt = OCGT(WILDCARD, 0) context.generators = [coal] + _pumped_hydro() + _hydro() + [ocgt]
def re100(context)
-
100% renewable electricity.
Expand source code
def re100(context): """100% renewable electricity.""" result = [] # The following list is in merit order. for g in [PV1Axis, Wind, WindOffshore, PumpedHydroTurbine, Hydro, CentralReceiver, Biofuel]: if g == PumpedHydroTurbine: result += _pumped_hydro() elif g == Hydro: result += _hydro() elif g == WindOffshore: cfg = configfile.get('generation', 'offshore-wind-trace') for column, poly in enumerate([31, 36, 38, 40]): result.append(g(poly, 0, cfg, column, build_limit=offshore_wind_limit[poly], label='polygon {poly} offshore')) elif g in [Biofuel, PV1Axis, CentralReceiver, Wind]: result += _every_poly(g) else: raise UnreachableError('unhandled generator type') context.generators = result
def re100_dsp(context)
-
Mostly renewables with demand side participation.
Expand source code
def re100_dsp(context): """Mostly renewables with demand side participation.""" re100(context) context.generators += _demand_response()
def re100_nocst(context)
-
100% renewables, but no CST.
Expand source code
def re100_nocst(context): """100% renewables, but no CST.""" re100(context) newlist = [g for g in context.generators if not isinstance(g, CST)] context.generators = newlist
def re100_nsw(context)
-
100% renewables in New South Wales only.
Expand source code
def re100_nsw(context): """100% renewables in New South Wales only.""" re100_one_region(context, regions.nsw)
def re100_one_region(context, region)
-
100% renewables in one region only.
Expand source code
def re100_one_region(context, region): """100% renewables in one region only.""" re100(context) context.regions = [region] wind, pv, cst = _one_per_poly(region) newlist = wind newlist += pv newlist += [g for g in context.generators if isinstance(g, Hydro) and g.region() is region] newlist += cst newlist += [g for g in context.generators if isinstance(g, Biofuel) and g.region() is region] context.generators = newlist
def re100_qld(context)
-
100% renewables in Queensland only.
Expand source code
def re100_qld(context): """100% renewables in Queensland only.""" re100_one_region(context, regions.qld)
def re100_south_aus(context)
-
100% renewables in South Australia only.
Expand source code
def re100_south_aus(context): """100% renewables in South Australia only.""" re100_one_region(context, regions.sa)
def re_plus_ccs(context)
-
Mostly renewables with fossil and CCS augmentation.
Expand source code
def re_plus_ccs(context): """Mostly renewables with fossil and CCS augmentation.""" re100(context) coal = Black_Coal(WILDCARD, 0) # pylint: disable=redefined-outer-name coal_ccs = Coal_CCS(WILDCARD, 0) # pylint: disable=redefined-outer-name ccgt = CCGT(WILDCARD, 0) ccgt_ccs = CCGT_CCS(WILDCARD, 0) ocgt = OCGT(WILDCARD, 0) context.generators = [coal, coal_ccs, ccgt, ccgt_ccs] + \ context.generators[:-4] + [ocgt]
def re_plus_fossil(context)
-
Mostly renewables with some fossil augmentation.
Expand source code
def re_plus_fossil(context): """Mostly renewables with some fossil augmentation.""" re100(context) context.generators = \ [Black_Coal(WILDCARD, 0), CCGT(WILDCARD, 0)] + \ context.generators[:-4] + [OCGT(WILDCARD, 0)]
def replacement(context)
-
Replace the current NEM fleet, more or less.
Expand source code
def replacement(context): """Replace the current NEM fleet, more or less.""" context.generators = \ [Black_Coal(WILDCARD, 0)] + _pumped_hydro() + _hydro() + \ [OCGT(WILDCARD, 0)]