Exemple #1
0
 def test_good_scenario(self):
     mock_plant = {
         "plant_id": ["A", "B", "C", "D"],
         "ramp_30": [2.5, 5, 10, 25],
     }
     mock_scenario = MockScenario({"plant": mock_plant})
     _check_scenario_is_in_analyze_state(mock_scenario)
Exemple #2
0
def calculate_NLDC(scenario, resources, hours=100):
    """Calculate the capacity value of a class of resources by comparing the
    mean of the top N hour of absolute demand to the mean of the top N hours of
    net demand. NLDC = 'Net Load Duration Curve'.

    :param powersimdata.scenario.scenario.Scenario scenario: analyzed scenario.
    :param str/list/tuple/set resources: one or more resources to analyze.
    :param int hours: number of hours to analyze.
    :return: (*float*) -- difference between peak demand and peak net demand.
    """
    _check_scenario_is_in_analyze_state(scenario)
    grid = scenario.state.get_grid()
    resources = _check_resources_are_in_grid_and_format(resources, grid)
    _check_number_hours_to_analyze(scenario, hours)

    # Then calculate capacity value
    total_demand = scenario.state.get_demand().sum(axis=1)
    prev_peak = total_demand.sort_values(ascending=False).head(hours).mean()
    plant_groupby = grid.plant.groupby("type")
    plant_indices = sum(
        [plant_groupby.get_group(r).index.tolist() for r in resources], [])
    resource_generation = scenario.state.get_pg()[plant_indices].sum(axis=1)
    net_demand = total_demand - resource_generation
    net_peak = net_demand.sort_values(ascending=False).head(hours).mean()
    return prev_peak - net_peak
Exemple #3
0
def generate_emissions_stats(scenario, pollutant="carbon", method="simple"):
    """Generate emissions statistics from the input generation data. Method
    descriptions: 'simple' uses a fixed ratio of CO2 to MWh, 'always-on' uses
    generator heat-rate curves including non-zero intercepts, 'decommit' uses
    generator heat-rate curves but de-commits generators if they are off
    (detected by pg < 1 MW).

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param str pollutant: pollutant to analyze.
    :param str method: selected method to handle no-load fuel consumption.
    :return: (*pandas.DataFrame*) -- emissions data frame.
    """
    _check_scenario_is_in_analyze_state(scenario)
    mi = ModelImmutables(scenario.info["grid_model"])
    allowed_methods = {
        "carbon": {"simple", "always-on", "decommit"},
        "nox": {"simple"},
        "so2": {"simple"},
    }
    emissions_per_mwh = {
        "carbon": mi.plants["carbon_per_mwh"],
        "nox": mi.plants["nox_per_mwh"],
        "so2": mi.plants["so2_per_mwh"],
    }

    if pollutant not in allowed_methods.keys():
        raise ValueError("Unknown pollutant for generate_emissions_stats()")
    if not isinstance(method, str):
        raise TypeError("method must be a str")
    if method not in allowed_methods[pollutant]:
        err_msg = f"method for {pollutant} must be one of: {allowed_methods[pollutant]}"
        raise ValueError(err_msg)

    pg = scenario.state.get_pg()
    grid = scenario.state.get_grid()
    emissions = pd.DataFrame(np.zeros_like(pg),
                             index=pg.index,
                             columns=pg.columns)

    if method == "simple":
        for fuel, val in emissions_per_mwh[pollutant].items():
            indices = (grid.plant["type"] == fuel).to_numpy()
            emissions.loc[:, indices] = pg.loc[:, indices] * val / 1000
    elif method in ("decommit", "always-on"):
        decommit = True if method == "decommit" else False

        costs = calc_costs(pg, grid.gencost["before"], decommit=decommit)
        heat = np.zeros_like(costs)

        for fuel, val in mi.plants["carbon_per_mmbtu"].items():
            indices = (grid.plant["type"] == fuel).to_numpy()
            heat[:, indices] = (costs[:, indices] /
                                grid.plant["GenFuelCost"].values[indices])
            emissions.loc[:, indices] = heat[:, indices] * val * 44 / 12 / 1000
    else:
        raise Exception("I should not be able to get here")

    return emissions
Exemple #4
0
 def test_bad_scenario_state(self):
     mock_plant = {
         "plant_id": ["A", "B", "C", "D"],
         "ramp_30": [2.5, 5, 10, 25],
     }
     mock_scenario = MockScenario({"plant": mock_plant})
     mock_scenario.state = "Create"
     with self.assertRaises(ValueError):
         _check_scenario_is_in_analyze_state(mock_scenario)
Exemple #5
0
def pmin_constraints(scenario, epsilon=1e-3):
    """Identify time periods in which generators are at minimum power.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param float epsilon: allowable 'fuzz' for whether constraint is binding.
    :return: (*pandas.DataFrame*) -- Boolean data frame of same shape as PG.
    """
    _check_scenario_is_in_analyze_state(scenario)
    _check_epsilon(epsilon)

    pg = scenario.state.get_pg()
    grid = scenario.state.get_grid()
    pmin = grid.plant["Pmin"]
    binding_pmin_constraints = (pg - pmin) <= epsilon

    return binding_pmin_constraints
Exemple #6
0
def ramp_constraints(scenario, epsilon=1e-3):
    """Identify time periods in which generators have binding ramp constraints.
    .. note:: The first time period will always return *False* for each column.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param float epsilon: allowable 'fuzz' for whether constraint is binding.
    :return: (*pandas.DataFrame*) -- Boolean dataframe of same shape as PG.
    """
    _check_scenario_is_in_analyze_state(scenario)
    _check_epsilon(epsilon)

    pg = scenario.state.get_pg()
    grid = scenario.state.get_grid()
    ramp = grid.plant["ramp_30"]
    diff = pg.diff(axis=0)
    binding_ramp_constraints = (ramp * 2 - abs(diff)) <= epsilon

    return binding_ramp_constraints
Exemple #7
0
def calculate_curtailment_time_series(scenario):
    """Calculate a time series of curtailment for renewable resources.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :return: (*pandas.DataFrame*) -- time series of curtailment
    """
    _check_scenario_is_in_analyze_state(scenario)
    grid = scenario.state.get_grid()
    pg = scenario.state.get_pg()

    plant_id = get_plant_id_for_resources(
        grid.model_immutables.plants["renewable_resources"].intersection(
            set(grid.plant.type)
        ),
        grid,
    )
    profiles = pd.concat(
        [scenario.state.get_solar(), scenario.state.get_wind()], axis=1
    )

    curtailment = (profiles[plant_id] - pg[plant_id]).clip(lower=0).round(6)
    return curtailment
Exemple #8
0
def calculate_net_load_peak(scenario, resources, hours=100):
    """Calculate the capacity value of a class of resources by averaging the
    power generated in the top N hours of net load peak.

    :param powersimdata.scenario.scenario.Scenario scenario: analyzed scenario.
    :param str/list/tuple/set resources: one or more resources to analyze.
    :param int hours: number of hours to analyze.
    :return: (*float*) -- resource capacity during hours of peak net demand.
    """
    _check_scenario_is_in_analyze_state(scenario)
    grid = scenario.state.get_grid()
    resources = _check_resources_are_in_grid_and_format(resources, grid)
    _check_number_hours_to_analyze(scenario, hours)

    # Then calculate capacity value
    total_demand = scenario.state.get_demand().sum(axis=1)
    plant_groupby = grid.plant.groupby("type")
    plant_indices = sum(
        [plant_groupby.get_group(r).index.tolist() for r in resources], [])
    resource_generation = scenario.state.get_pg()[plant_indices].sum(axis=1)
    net_demand = total_demand - resource_generation
    top_hours = net_demand.sort_values(ascending=False).head(hours).index
    return resource_generation[top_hours].mean()
def map_plant_capacity(scenario, us_states_dat=None, size_factor=1):
    """Makes map of renewables from change table 'new plants'. Size/area
    indicates capacity.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance.
    :param dict us_states_dat: dictionary of state border lats/lons. If None, get
        from :func:`postreise.plot.plot_states.get_state_borders`.
    :param float/int size_factor: scale size of glyphs.
    """

    _check_scenario_is_in_analyze_state(scenario)

    # prepare data from the change table to select new plants
    ct = scenario.state.get_ct()
    # check that there are new plants, check scenario is in analyze state
    if "new_plant" not in ct.keys():
        raise ValueError(
            "There are no new plants added in the selected scenario. Please choose a different scenario."
        )
    if us_states_dat is None:
        us_states_dat = get_state_borders()

    newplant = pd.DataFrame(ct["new_plant"])
    newplant = newplant.set_index("bus_id")
    newplant = newplant[newplant.Pmax > 0]

    # merge with bus info to get coordinates
    gridscen = scenario.state.get_grid()
    bus_of_interest = gridscen.bus.loc[list(set(newplant.index))]
    bus_capacity = bus_of_interest.merge(newplant,
                                         right_index=True,
                                         left_index=True)

    bus_map = project_bus(bus_capacity)
    bus_map1 = bus_map.loc[bus_map.Pmax > 1]
    rar_df = bus_map1.loc[(bus_map1.type_y == "solar") |
                          (bus_map1.type_y == "wind")]

    # group by coordinates
    rar_df = rar_df.groupby(["lat", "lon"]).agg({
        "Pmax": "sum",
        "x": "mean",
        "y": "mean"
    })

    a, b = project_borders(us_states_dat)

    rar_source = ColumnDataSource({
        "x": rar_df["x"],
        "y": rar_df["y"],
        "capacity": (rar_df["Pmax"] * size_factor)**0.5,
        "capacitymw": rar_df["Pmax"],
    })

    tools: str = "pan,wheel_zoom,reset,save"
    p = figure(
        tools=tools,
        x_axis_location=None,
        y_axis_location=None,
        plot_width=800,
        plot_height=800,
        output_backend="webgl",
        sizing_mode="stretch_both",
        match_aspect=True,
    )

    # for legend, plot behind tiles
    p.circle(
        -8.1e6,
        5.2e6,
        fill_color=be_green,
        color=be_green,
        alpha=0.5,
        size=50,
        legend_label="Renewable Capacity Added",
    )

    p.add_tile(get_provider(Vendors.CARTODBPOSITRON_RETINA))

    # state borders
    p.patches(a, b, fill_alpha=0.0, line_color="gray", line_width=2)

    # capacity circles
    circle = p.circle(
        "x",
        "y",
        fill_color=be_green,
        color=be_green,
        alpha=0.8,
        size="capacity",
        source=rar_source,
    )
    p.legend.label_text_font_size = "12pt"
    p.legend.location = "bottom_right"

    hover = HoverTool(
        tooltips=[
            ("Capacity (MW)", "@capacitymw"),
        ],
        renderers=[circle],
    )

    p.add_tools(hover)

    return p
Exemple #10
0
def plot_generation_time_series_stack(
    scenario,
    area,
    resources,
    area_type=None,
    time_range=None,
    time_zone="utc",
    time_freq="H",
    show_demand=True,
    show_net_demand=True,
    normalize=False,
    t2c=None,
    t2l=None,
    t2hc=None,
    title=None,
    label_fontsize=20,
    title_fontsize=22,
    tick_fontsize=15,
    legend_fontsize=18,
    save=False,
    filename=None,
    filepath=None,
):
    """Generate time series generation stack plot in a certain area of a scenario.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance
    :param str area: one of *loadzone*, *state*, *state abbreviation*,
        *interconnect*, *'all'*
    :param str/list resources: one or a list of resources. *'solar_curtailment'*,
        *'wind_curtailment'*, *'wind_offshore_curtailment'* are valid entries together
        with all available generator types in the area. The order of the resources
        determines the stack order in the figure.
    :param str area_type: one of *'loadzone'*, *'state'*, *'state_abbr'*,
        *'interconnect'*
    :param tuple time_range: [start_timestamp, end_timestamp] where each time stamp
        is pandas.Timestamp/numpy.datetime64/datetime.datetime. If None, the entire
        time range is used for the given scenario.
    :param str time_zone: new time zone.
    :param str time_freq: frequency. Either *'D'* (day), *'W'* (week), *'M'* (month).
    :param bool show_demand: show demand line in the plot or not, default is True.
    :param bool show_net_demand: show net demand line in the plot or not, default is
        True.
    :param bool normalize: normalize the generation based on capacity or not,
        default is False.
    :param dict t2c: user specified color of resource type to overwrite type2color
        default dict. key: resource type, value: color code.
    :param dict t2l: user specified label of resource type to overwrite type2label
        default dict. key: resource type, value: label.
    :param dict t2hc: user specified color of curtailable resource hatches to overwrite
        type2hatchcolor default dict. key: resource type, valid keys are
        *'wind_curtailment'*, *'solar_curtailment'*, *'wind_offshore_curtailment'*,
        value: color code.
    :param str title: user specified title of the figure, default is set to be area.
    :param float label_fontsize: user specified label fontsize, default is 20.
    :param float title_fontsize: user specified title fontsize, default is 22.
    :param float tick_fontsize: user specified ticks of axes fontsize, default is 15.
    :param float legend_fontsize: user specified legend fontsize, default is 18.
    :param bool save: save the generated figure or not, default is False.
    :param str filename: if save is True, user specified filename, use area if None.
    :param str filepath: if save is True, user specified filepath, use current
        directory if None.
    """
    _check_scenario_is_in_analyze_state(scenario)

    mi = ModelImmutables(scenario.info["grid_model"])
    type2color = mi.plants["type2color"]
    type2label = mi.plants["type2label"]
    type2hatchcolor = mi.plants["type2hatchcolor"]
    if t2c:
        type2color.update(t2c)
    if t2l:
        type2label.update(t2l)
    if t2hc:
        type2hatchcolor.update(t2hc)

    pg_stack = get_generation_time_series_by_resources(scenario,
                                                       area,
                                                       resources,
                                                       area_type=area_type)
    capacity = get_capacity_by_resources(scenario,
                                         area,
                                         resources,
                                         area_type=area_type)
    demand = get_demand_time_series(scenario, area, area_type=area_type)
    net_demand = get_net_demand_time_series(scenario,
                                            area,
                                            area_type=area_type)
    capacity_ts = pd.Series(capacity.sum(), index=pg_stack.index)

    curtailable_resources = {
        "solar_curtailment",
        "wind_curtailment",
        "wind_offshore_curtailment",
    }
    if curtailable_resources & set(resources):
        curtailment = get_curtailment_time_series(scenario,
                                                  area,
                                                  area_type=area_type)
        for r in curtailable_resources:
            if r in resources and r in curtailment.columns:
                pg_stack[r] = curtailment[r]

    if time_zone != "utc":
        pg_stack = change_time_zone(pg_stack, time_zone)
        demand = change_time_zone(demand, time_zone)
        net_demand = change_time_zone(net_demand, time_zone)
        capacity_ts = change_time_zone(capacity_ts, time_zone)
    if not time_range:
        time_range = (
            pd.Timestamp(scenario.info["start_date"]),
            pd.Timestamp(scenario.info["end_date"]),
        )
    pg_stack = slice_time_series(pg_stack, time_range[0], time_range[1])
    demand = slice_time_series(demand, time_range[0], time_range[1])
    net_demand = slice_time_series(net_demand, time_range[0], time_range[1])
    capacity_ts = slice_time_series(capacity_ts, time_range[0], time_range[1])
    if time_freq != "H":
        pg_stack = resample_time_series(pg_stack, time_freq)
        demand = resample_time_series(demand, time_freq)
        net_demand = resample_time_series(net_demand, time_freq)
        capacity_ts = resample_time_series(capacity_ts, time_freq)

    if "storage" in resources:
        pg_storage = get_storage_time_series(scenario,
                                             area,
                                             area_type=area_type)
        capacity_storage = get_storage_capacity(scenario,
                                                area,
                                                area_type=area_type)
        capacity_storage_ts = pd.Series(capacity_storage,
                                        index=pg_storage.index)

        if time_zone != "utc":
            pg_storage = change_time_zone(pg_storage, time_zone)
            capacity_storage_ts = change_time_zone(capacity_storage_ts,
                                                   time_zone)
        if time_range != ("2016-01-01 00:00:00", "2016-12-31 23:00:00"):
            pg_storage = slice_time_series(pg_storage, time_range[0],
                                           time_range[1])
            capacity_storage_ts = slice_time_series(capacity_storage_ts,
                                                    time_range[0],
                                                    time_range[1])
        if time_freq != "H":
            pg_storage = resample_time_series(pg_storage, time_freq)
            capacity_storage_ts = resample_time_series(capacity_storage_ts,
                                                       time_freq)

        pg_stack["storage"] = pg_storage.clip(lower=0)
        capacity_ts += capacity_storage_ts

        fig, (ax, ax_storage) = plt.subplots(
            2,
            1,
            figsize=(20, 15),
            sharex="row",
            gridspec_kw={
                "height_ratios": [3, 1],
                "hspace": 0.02
            },
        )
        plt.subplots_adjust(wspace=0)
        if normalize:
            pg_storage = pg_storage.divide(capacity_storage_ts, axis="index")
            ax_storage.set_ylabel("Normalized Storage",
                                  fontsize=label_fontsize)
        else:
            ax_storage.set_ylabel("Energy Storage (MW)",
                                  fontsize=label_fontsize)

        ax_storage = pg_storage.plot(color=type2color["storage"],
                                     lw=4,
                                     ax=ax_storage)
        ax_storage.fill_between(
            pg_storage.index.values,
            0,
            pg_storage.values,
            color=type2color["storage"],
            alpha=0.5,
        )

        # Erase year in xticklabels
        xt_with_year = list(ax_storage.__dict__["date_axis_info"][0])
        xt_with_year[-1] = b"%b"
        ax_storage.__dict__["date_axis_info"][0] = tuple(xt_with_year)

        ax_storage.tick_params(axis="both",
                               which="both",
                               labelsize=tick_fontsize)
        ax_storage.set_xlabel("")
        for a in fig.get_axes():
            a.label_outer()
    else:
        fig = plt.figure(figsize=(20, 10))
        ax = fig.gca()

    if normalize:
        pg_stack = pg_stack.divide(capacity_ts, axis="index")
        demand = demand.divide(capacity_ts, axis="index")
        net_demand = net_demand.divide(capacity_ts, axis="index")
        ax.set_ylabel("Normalized Generation", fontsize=label_fontsize)
    else:
        pg_stack = pg_stack.divide(1e6, axis="index")
        demand = demand.divide(1e6, axis="index")
        net_demand = net_demand.divide(1e6, axis="index")
        ax.set_ylabel("Daily Energy TWh", fontsize=label_fontsize)

    available_resources = [r for r in resources if r in pg_stack.columns]
    pg_stack[available_resources].clip(0, None).plot.area(color=type2color,
                                                          linewidth=0,
                                                          alpha=0.7,
                                                          ax=ax,
                                                          sharex="row")

    if show_demand:
        demand.plot(color="red", lw=4, ax=ax)
    if show_net_demand:
        net_demand.plot(color="red", ls="--", lw=2, ax=ax)

    if not title:
        title = area
    ax.set_title("%s" % title, fontsize=title_fontsize)
    ax.grid(color="black", axis="y")

    if "storage" not in resources:
        # Erase year in xticklabels
        xt_with_year = list(ax.__dict__["date_axis_info"][0])
        xt_with_year[-1] = b"%b"
        ax.__dict__["date_axis_info"][0] = tuple(xt_with_year)
        ax.set_xlabel("")

    ax.tick_params(which="both", labelsize=tick_fontsize)
    ax.set_ylim([
        min(0, 1.1 * net_demand.min()),
        max(ax.get_ylim()[1], 1.1 * demand.max()),
    ])

    handles, labels = ax.get_legend_handles_labels()
    if show_demand:
        labels[0] = "Demand"
    if show_net_demand:
        labels[1] = "Net Demand"
    label_offset = show_demand + show_net_demand
    labels = [type2label[l] if l in type2label else l for l in labels]

    # Add hatches
    for r in curtailable_resources:
        if r in available_resources:
            ind = available_resources.index(r)
            ax.fill_between(
                pg_stack[available_resources].index.values,
                pg_stack[available_resources].iloc[:, :ind + 1].sum(axis=1),
                pg_stack[available_resources].iloc[:, :ind].sum(axis=1),
                color="none",
                hatch="//",
                edgecolor=type2hatchcolor[r],
                linewidth=0.0,
            )
            handles[ind + label_offset] = mpatches.Patch(
                facecolor=type2color[r],
                hatch="//",
                edgecolor=type2hatchcolor[r],
                linewidth=0.0,
            )

    ax.legend(
        handles[::-1],
        labels[::-1],
        frameon=2,
        prop={"size": legend_fontsize},
        loc="upper left",
        bbox_to_anchor=(1, 1),
    )

    if save:
        if not filename:
            filename = area
        if not filepath:
            filepath = os.path.join(os.getcwd(), filename)
        plt.savefig(f"{filepath}.pdf", bbox_inches="tight", pad_inches=0)
Exemple #11
0
def test_check_scenario_is_in_analyze():
    _check_scenario_is_in_analyze_state(scenario)
Exemple #12
0
def test_check_scenario_is_in_analyze_state_argument_value():
    input = MockScenario()
    input.state = "Create"
    with pytest.raises(ValueError):
        _check_scenario_is_in_analyze_state(input)
Exemple #13
0
def test_check_scenario_is_in_analyze_state_argument_type():
    arg = (1, grid)
    for a in arg:
        with pytest.raises(TypeError):
            _check_scenario_is_in_analyze_state(a)
Exemple #14
0
def plot_curtailment_time_series(
    scenario,
    area,
    resources,
    area_type=None,
    time_range=None,
    time_zone="utc",
    time_freq="H",
    show_demand=True,
    percentage=True,
    t2c=None,
    t2l=None,
    title=None,
    label_fontsize=20,
    title_fontsize=22,
    tick_fontsize=15,
    legend_fontsize=18,
    save=False,
    filename=None,
    filepath=None,
):
    """Generate time series curtailment plot of each specified resource in a certain
    area of a scenario.

    :param powersimdata.scenario.scenario.Scenario scenario: scenario instance
    :param str area: one of *loadzone*, *state*, *state abbreviation*,
        *interconnect*, *'all'*
    :param str/list resources: one or a list of resources.
    :param str area_type: one of *'loadzone'*, *'state'*, *'state_abbr'*,
        *'interconnect'*
    :param tuple time_range: [start_timestamp, end_timestamp] where each time stamp
        is pandas.Timestamp/numpy.datetime64/datetime.datetime. If None, the entire
        time range is used for the given scenario.
    :param str time_zone: new time zone.
    :param str time_freq: frequency. Either *'D'* (day), *'W'* (week), *'M'* (month).
    :param bool show_demand: show demand line in the plot or not, default is True.
    :param bool percentage: plot the curtailment in terms of percentage or not,
        default is True.
    :param dict t2c: user specified color of resource type to overwrite type2color
        default dict. key: resource type, value: color code.
    :param dict t2l: user specified label of resource type to overwrite type2label
        default dict. key: resource type, value: label.
    :param str title: user specified title of the figure.
    :param float label_fontsize: user specified label fontsize, default is 20.
    :param float title_fontsize: user specified title fontsize, default is 22.
    :param float tick_fontsize: user specified ticks of axes fontsize, default is 15.
    :param float legend_fontsize: user specified legend fontsize, default is 18.
    :param bool save: save the generated figure or not, default is False.
    :param str filename: if save is True, user specified filename, use area if None.
    :param str filepath: if save is True, user specified filepath, use current
        directory if None.
    """
    _check_scenario_is_in_analyze_state(scenario)
    resources = _check_resources_and_format(
        resources, grid_model=scenario.info["grid_model"]
    )

    mi = ModelImmutables(scenario.info["grid_model"])
    type2color = mi.plants["type2color"]
    type2label = mi.plants["type2label"]
    if t2c:
        type2color.update(t2c)
    if t2l:
        type2label.update(t2l)

    resource_pg = get_generation_time_series_by_resources(
        scenario, area, resources, area_type=area_type
    )
    demand = get_demand_time_series(scenario, area, area_type=area_type)
    curtailment = get_curtailment_time_series(scenario, area, area_type=area_type)

    if time_zone != "utc":
        resource_pg = change_time_zone(resource_pg, time_zone)
        demand = change_time_zone(demand, time_zone)
        curtailment = change_time_zone(curtailment, time_zone)
    if not time_range:
        time_range = (
            pd.Timestamp(scenario.info["start_date"]),
            pd.Timestamp(scenario.info["end_date"]),
        )
    resource_pg = slice_time_series(resource_pg, time_range[0], time_range[1])
    demand = slice_time_series(demand, time_range[0], time_range[1])
    curtailment = slice_time_series(curtailment, time_range[0], time_range[1])
    if time_freq != "H":
        resource_pg = resample_time_series(resource_pg, time_freq)
        demand = resample_time_series(demand, time_freq)
        curtailment = resample_time_series(curtailment, time_freq)

    for r in resource_pg.columns:
        curtailment[r + "_curtailment" + "_mean"] = curtailment[
            r + "_curtailment"
        ].mean()
        curtailment[r + "_available"] = curtailment[r + "_curtailment"] + resource_pg[r]
        curtailment[r + "_curtailment" + "_percentage"] = (
            curtailment[r + "_curtailment"] / curtailment[r + "_available"] * 100
        )
        curtailment[r + "_curtailment" + "_percentage" + "_mean"] = curtailment[
            r + "_curtailment" + "_percentage"
        ].mean()

    for r in resources:
        if r not in resource_pg.columns:
            raise ValueError(f"{r} is invalid in {area}!")
        fig = plt.figure(figsize=(20, 10))
        ax = fig.gca()

        title_text = f"{area} {r.capitalize()}" if not title else title
        plt.title(title_text, fontsize=title_fontsize)

        cr = r + "_curtailment"
        if percentage:
            key1, key2 = f"{cr}_percentage", f"{cr}_percentage_mean"
        else:
            key1, key2 = cr, f"{cr}_mean"
        curtailment[key1].plot(
            ax=ax,
            lw=4,
            alpha=0.7,
            color=type2color[cr],
            label=type2label[cr],
        )
        curtailment[key2].plot(
            ax=ax,
            ls="--",
            lw=4,
            alpha=0.7,
            color=type2color[cr],
            label=type2label[cr] + " Mean",
        )

        ax_twin = ax.twinx()
        curtailment[r + "_available"].plot(
            ax=ax_twin,
            lw=4,
            alpha=0.7,
            color=type2color[r],
            label=f"{type2label[r]} Energy Available",
        )

        if show_demand:
            demand.plot(ax=ax_twin, lw=4, alpha=0.7, color="red", label="Demand")

        # Erase year in xticklabels
        xt_with_year = list(ax_twin.__dict__["date_axis_info"][0])
        xt_with_year[-1] = b"%b"
        ax_twin.__dict__["date_axis_info"][0] = tuple(xt_with_year)
        ax_twin.set_xlabel("")
        ax_twin.tick_params(which="both", labelsize=tick_fontsize)
        ax_twin.yaxis.get_offset_text().set_fontsize(tick_fontsize)
        ax_twin.set_ylabel("MWh", fontsize=label_fontsize)
        ax_twin.legend(loc="upper right", prop={"size": legend_fontsize})

        ax.tick_params(which="both", labelsize=tick_fontsize)
        ax.yaxis.get_offset_text().set_fontsize(tick_fontsize)
        ax.grid(color="black", axis="y")
        ax.set_xlabel("")
        if percentage:
            ax.set_ylabel("Curtailment [%]", fontsize=label_fontsize)
        else:
            ax.set_ylabel("Curtailment", fontsize=label_fontsize)
        ax.legend(loc="upper left", prop={"size": legend_fontsize})

        if save:
            if not filename:
                filename = f"{area.lower()}_{r}_curtailment"
            if not filepath:
                filepath = os.path.join(os.getcwd(), filename)
            plt.savefig(f"{filepath}.pdf", bbox_inches="tight", pad_inches=0)
Exemple #15
0
 def test_bad_scenario_type(self):
     with self.assertRaises(TypeError):
         _check_scenario_is_in_analyze_state("307")