Exemple #1
0
def _one_per_poly(region):
    """Return three lists of wind, PV and CST generators, one per polygon.

    >>> from nemo import regions
    >>> wind, pv, cst = _one_per_poly(regions.tas)
    >>> len(wind), len(pv), len(cst)
    (4, 4, 4)
    """
    pv = []
    wind = []
    cst = []

    for poly in region.polygons:
        wind.append(generators.Wind(poly, 0,
                                    configfile.get('generation', 'wind-trace'),
                                    poly - 1,
                                    delimiter=',',
                                    build_limit=polygons.wind_limit[poly],
                                    label='poly %d wind' % poly))
        pv.append(generators.PV1Axis(poly, 0,
                                     configfile.get('generation', 'pv1axis-trace'),
                                     poly - 1,
                                     build_limit=polygons.pv_limit[poly],
                                     label='poly %d PV' % poly))
        cst.append(generators.CentralReceiver(poly, 0, 2, 6,
                                              configfile.get('generation', 'cst-trace'),
                                              poly - 1,
                                              build_limit=polygons.cst_limit[poly],
                                              label='poly %d CST' % poly))
    return wind, pv, cst
Exemple #2
0
def theworks(context):
    """All technologies."""

    re100(context)
    # pylint: disable=redefined-outer-name
    egs = generators.Geothermal_EGS(polygons.wildcard, 0,
                                    configfile.get('generation', 'egs-geothermal-trace'), 38)
    hsa = generators.Geothermal_HSA(polygons.wildcard, 0,
                                    configfile.get('generation', 'hsa-geothermal-trace'), 38)
    pt = generators.ParabolicTrough(polygons.wildcard, 0, 2, 6,
                                    configfile.get('generation', 'cst-trace'), 12)
    coal = generators.Black_Coal(polygons.wildcard, 0)
    coal_ccs = generators.Coal_CCS(polygons.wildcard, 0)
    ccgt = generators.CCGT(polygons.wildcard, 0)
    ccgt_ccs = generators.CCGT_CCS(polygons.wildcard, 0)
    ocgt = generators.OCGT(polygons.wildcard, 0)
    batt = generators.Battery(polygons.wildcard, 0, 0)
    diesel = generators.Diesel(polygons.wildcard, 0)
    dem = generators.DemandResponse(polygons.wildcard, 0, 300)
    biomass = generators.Biomass(polygons.wildcard, 0)
    greenpower = generators.GreenPower(polygons.wildcard, 0)
    btm_pv = generators.Behind_Meter_PV(polygons.wildcard, 0,
                                        configfile.get('generation', 'rooftop-pv-trace'),
                                        0)
    g = context.generators

    context.generators = [hsa, egs, pt, coal, coal_ccs, ccgt, ccgt_ccs] + g[:-4] + \
                         [btm_pv, ocgt, diesel, batt, dem, biomass, greenpower]
Exemple #3
0
def plot(context, spills=False, filename=None, showlegend=True, xlim=None):
    """Produce a pretty plot of supply and demand."""
    # aggregate demand
    demand = context.demand.sum(axis=1)

    plt.clf()
    plt.ylabel('Power (MW)')
    try:
        title = configfile.get('plot', 'title')
    except (configparser.NoSectionError, configparser.NoOptionError):
        title = 'Supply/demand balance'
    try:
        title += '\n' + configfile.get('plot', 'subtitle')
    except (configfile.configparser.NoSectionError, configfile.configparser.NoOptionError):
        pass
    plt.suptitle(title)

    if showlegend:
        _legend(context)

    # Plot demand first.
    plt.plot(demand.index, demand, color='black', linewidth=3 if spills else 2)

    accum = pd.Series(data=0, index=demand.index)
    prev = accum.copy()
    for g in _generator_list(context):
        idx = context.generators.index(g)
        accum += context.generation[idx]
        # Ensure accumulated generation does not exceed demand in any timestep.
        # (Due to rounding, accum can be close to demand.)
        assert all(np.logical_or(accum < demand, np.isclose(accum, demand)))
        plt.plot(accum.index, accum, color='black', linewidth=0.5)
        plt.fill_between(accum.index, prev, accum,
                         facecolor=g.patch.get_fc(),
                         hatch=g.patch.get_hatch())
        prev = accum.copy()
    # Unmet demand is shaded red.
    plt.fill_between(accum.index, accum, demand, facecolor='red')

    if spills:
        prev = demand.copy()
        for g in list(g for g in context.generators if g.region() in context.regions):
            idx = context.generators.index(g)
            accum += context.spill[idx]
            plt.plot(accum.index, accum, color='black', linewidth=0.5)
            plt.fill_between(prev.index, prev, accum, facecolor=g.patch.get_fc(), alpha=0.3)
            prev = accum.copy()

    plt.gca().set_xlim(xlim)  # set_xlim accepts None
    plt.gca().xaxis_date()
    plt.gcf().autofmt_xdate()

    _, ymax = plt.gca().get_ylim()
    plt.plot(context.unserved.index, [ymax] * len(context.unserved),
             "yv", markersize=10, color='red', markeredgecolor='black')

    if not filename:
        plt.show()  # pragma: no cover
    else:
        plt.savefig(filename)
Exemple #4
0
def re100(context):
    """100% renewable electricity.

    >>> class C: pass
    >>> c = C()
    >>> re100(c)
    >>> len(c.generators)
    184
    """
    from nemo.generators import CentralReceiver, Wind, PV1Axis, Hydro, PumpedHydro, Biofuel

    result = []
    # The following list is in merit order.
    for g in [PV1Axis, Wind, PumpedHydro, Hydro, CentralReceiver, Biofuel]:
        if g == PumpedHydro:
            result += [h for h in _hydro() if isinstance(h, PumpedHydro)]
        elif g == Hydro:
            result += [
                h for h in _hydro()
                if isinstance(h, Hydro) and not isinstance(h, PumpedHydro)
            ]
        elif g == Biofuel:
            for poly in range(1, 44):
                result.append(g(poly, 0, label='polygon %d GT' % poly))
        elif g == PV1Axis:
            for poly in range(1, 44):
                result.append(
                    g(poly,
                      0,
                      configfile.get('generation', 'pv1axis-trace'),
                      poly - 1,
                      build_limit=polygons.pv_limit[poly],
                      label='polygon %d PV' % poly))
        elif g == CentralReceiver:
            for poly in range(1, 44):
                result.append(
                    g(poly,
                      0,
                      2,
                      6,
                      configfile.get('generation', 'cst-trace'),
                      poly - 1,
                      build_limit=polygons.cst_limit[poly],
                      label='polygon %d CST' % poly))
        elif g == Wind:
            for poly in range(1, 44):
                result.append(
                    g(poly,
                      0,
                      configfile.get('generation', 'wind-trace'),
                      poly - 1,
                      delimiter=',',
                      build_limit=polygons.wind_limit[poly],
                      label='polygon %d wind' % poly))
        else:  # pragma: no cover
            raise ValueError

    context.generators = result
Exemple #5
0
    def __init__(self):
        """Initialise a default context."""
        self.verbose = False
        self.track_exchanges = False
        self.regions = regions.All
        self.startdate = startdate
        # Number of timesteps is determined by the number of demand rows.
        self.hours = len(hourly_regional_demand)
        # Estimate the number of years from the number of simulation hours.
        if self.hours == 8760 or self.hours == 8784:
            self.years = 1
        else:
            self.years = self.hours / (365.25 * 24)

        self.relstd = 0.002  # 0.002% unserved energy
        self.generators = [
            generators.CCGT(polygons.wildcard, 20000),
            generators.OCGT(polygons.wildcard, 20000)
        ]
        self.demand = hourly_demand.copy()
        self.timesteps = len(self.demand)
        self.spill = pd.DataFrame()
        self.generation = pd.DataFrame()
        self.unserved = pd.DataFrame()
        # System non-synchronous penetration limit
        self.nsp_limit = float(configfile.get('limits', 'nonsync-penetration'))
        self.exchanges = np.zeros(
            (self.hours, polygons.numpolygons, polygons.numpolygons))
        self.costs = costs.NullCosts()
Exemple #6
0
def re100_geothermal_hsa(context):
    """100% renewables plus HSA geothermal.

    >>> class C: pass
    >>> c = C()
    >>> re100_geothermal_hsa(c)
    >>> isinstance(c.generators[0], generators.Geothermal_HSA)
    True
    """
    re100(context)
    g = context.generators
    poly = 38
    geo = generators.Geothermal_HSA(
        poly, 0, configfile.get('generation', 'hsa-geothermal-trace'), poly,
        'HSA geothermal')
    context.generators = [geo] + g
Exemple #7
0
def re100_geothermal_egs(context):
    """100% renewables plus EGS geothermal.

    >>> class C: pass
    >>> c = C()
    >>> re100_geothermal_egs(c)
    >>> isinstance(c.generators[0], generators.Geothermal)
    True
    """
    re100(context)
    g = context.generators
    poly = 14
    geo = generators.Geothermal_EGS(
        poly, 0, configfile.get('generation', 'egs-geothermal-trace'), poly,
        'EGS geothermal')
    context.generators = [geo] + g
Exemple #8
0
"""A National Electricity Market (NEM) simulation."""

import urllib.request
import urllib.error
import urllib.parse
import numpy as np
import pandas as pd

from nemo import configfile
from nemo import regions
from nemo import polygons

# Demand is in 30 minute intervals. NOTE: the number of rows in the
# demand file now dictates the number of timesteps in the simulation.

urlobj = urllib.request.urlopen(configfile.get('demand', 'demand-trace'))
demand = pd.read_csv(urlobj,
                     comment='#',
                     sep=',',
                     parse_dates=[['Date', 'Time']],
                     index_col='Date_Time')

# Check for date, time and n demand columns (for n regions).
assert len(demand.columns) == regions.numregions
# The number of rows must be even.
assert len(demand) % 2 == 0, "odd number of rows in half-hourly demand data"

# Check demand data starts at midnight
startdate = demand.index[0]
assert (startdate.hour, startdate.minute, startdate.second) == (0, 30, 0), \
    'demand data must start at midnight'
Exemple #9
0
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
"""A National Electricity Market (NEM) simulation."""

import urllib2
import numpy as np
import pandas as pd

from nemo import configfile
from nemo import regions
from nemo import polygons

# Demand is in 30 minute intervals. NOTE: the number of rows in the
# demand file now dictates the number of timesteps in the simulation.

urlobj = urllib2.urlopen(configfile.get('demand', 'demand-trace'))
demand = pd.read_csv(urlobj,
                     comment='#',
                     sep=',',
                     parse_dates=[['Date', 'Time']],
                     index_col='Date_Time')

# Check for date, time and n demand columns (for n regions).
assert len(demand.columns) == regions.numregions
# The number of rows must be even.
assert len(demand) % 2 == 0, "odd number of rows in half-hourly demand data"

# Check demand data starts at midnight
startdate = demand.index[0]
assert (startdate.hour, startdate.minute, startdate.second) == (0, 30, 0), \
    'demand data must start at midnight'
Exemple #10
0
from nemo import costs
from nemo import configfile as cf
from nemo import transmission

# Conversion factor between MWh and TWh.
twh = pow(10., 6)

# Ignore possible runtime warnings from SCOOP
warnings.simplefilter('ignore', RuntimeWarning)

parser = argparse.ArgumentParser(
    description='Bug reports to: [email protected]')
parser.add_argument("-c",
                    "--carbon-price",
                    type=int,
                    default=cf.get('costs', 'co2-price-per-t'),
                    help='carbon price ($/t) [default: %s]' %
                    cf.get('costs', 'co2-price-per-t'))
parser.add_argument("-d",
                    "--demand-modifier",
                    type=str,
                    default=[],
                    action="append",
                    help='demand modifier')
parser.add_argument("-g",
                    "--generations",
                    type=int,
                    default=cf.get('optimiser', 'generations'),
                    help='generations [default: %s]' %
                    cf.get('optimiser', 'generations'))
parser.add_argument("-o",