diff --git a/postreise/plot/plot_sim_vs_hist.py b/postreise/plot/plot_sim_vs_hist.py index 0b4e9daf..5660d02d 100644 --- a/postreise/plot/plot_sim_vs_hist.py +++ b/postreise/plot/plot_sim_vs_hist.py @@ -1,7 +1,8 @@ -import warnings - import matplotlib.pyplot as plt import numpy as np +import pandas as pd + +from postreise.analyze.generation.summarize import sum_generation_by_state class PlotSettings: @@ -10,41 +11,67 @@ class PlotSettings: size_inches = (20, 10) -def plot_generation_sim_vs_hist(sim_gen, hist_gen, s_info, state, showmax=True): - """ - Plot simulated vs historical generation of each resource in the scenario +def plot_generation_sim_vs_hist( + scenario, + hist_gen, + state, + show_max=True, + plot_show=True, +): + """Plot simulated vs historical generation of each resource in the scenario for the given state. Optionally include max generation. - :param pandas.DataFrame sim_gen: simulated generation dataframe - :param pandas.DataFrame hist_gen: historical generation dataframe - :param powersimdata.design.ScenarioInfo s_info: scenario info instance. + :param powersimdata.scenario.scenario.Scenario scenario: scenario instance. + :param pandas.DataFrame hist_gen: historical generation data frame. Columns are + resources and indices are state names. :param str state: the US state - :param bool showmax: determine whether to additionally plot max generation of each resource + :param bool show_max: determine whether to additionally plot max generation of each + resource. + :param plot_show: show the plot or not, defaults to True. + :return: (*matplotlib.axes.Axes*) -- axes object of the plot. + :raises TypeError: + if ``hist_gen`` is not a data frame. + if ``state`` is not a str. + :raises ValueError: if ``state`` is not in ``hist_gen`` or ``scenario``. """ - labels = list(sim_gen.columns) - all_resources = s_info.get_available_resource("all") + if not isinstance(hist_gen, pd.DataFrame): + raise TypeError("hist_gen must be a pandas.DataFrame") + if not isinstance(state, str): + raise TypeError("state must be a str") + + sim_gen = sum_generation_by_state(scenario) + if state not in sim_gen.index or state not in hist_gen.index: + raise ValueError("Invalid state") + + all_resources = list(sim_gen.columns) sim = [int(round(sim_gen.loc[state, res])) for res in all_resources] hist = [int(round(hist_gen.loc[state, res])) for res in all_resources] - def calc_max(gen_type): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return int( - s_info.get_capacity(gen_type, state) * len(s_info.pg.index) / 1000 - ) + grid = scenario.get_grid() + pg = scenario.get_pg() + + def calc_max(fuel): + loadzone = list(grid.model_immutables.area_to_loadzone(state, "state")) # noqa + capacity = grid.plant.query("type == @fuel & zone_name == @loadzone")[ + "Pmax" + ].sum() + if capacity == 0: + print(f"No {fuel} generator in {state}") + + return int(capacity * pg.shape[0] / 1000) max_gen = [] - if showmax: + if show_max: max_gen = [calc_max(res) for res in all_resources] - x = np.arange(len(labels)) # the label locations + x = np.arange(len(all_resources)) # the label locations width = PlotSettings.width fontsize = PlotSettings.fontsize size_inches_x, size_inches_y = PlotSettings.size_inches fig, ax = plt.subplots() - if showmax: + if show_max: rects1 = ax.bar(x - width, hist, width, label="Historical") rects2 = ax.bar(x, sim, width, label="Simulated") rects3 = ax.bar(x + width, max_gen, width, label="Max Generation") @@ -56,7 +83,7 @@ def calc_max(gen_type): ax.set_ylabel("GWh", fontsize=fontsize) ax.set_title(state, fontsize=fontsize) ax.set_xticks(x) - ax.set_xticklabels(labels, fontsize=fontsize) + ax.set_xticklabels(all_resources, fontsize=fontsize) ax.legend(fontsize=fontsize) def autolabel(rects): @@ -74,8 +101,10 @@ def autolabel(rects): autolabel(rects1) autolabel(rects2) - if showmax: + if show_max: autolabel(rects3) fig.tight_layout() fig.set_size_inches(size_inches_x, size_inches_y) - plt.show() + if plot_show: + plt.show() + return ax diff --git a/postreise/plot/tests/test_plot_sim_vs_hist.py b/postreise/plot/tests/test_plot_sim_vs_hist.py new file mode 100644 index 00000000..1bd1c7b6 --- /dev/null +++ b/postreise/plot/tests/test_plot_sim_vs_hist.py @@ -0,0 +1,78 @@ +import pandas as pd +import pytest +from powersimdata.tests.mock_scenario import MockScenario + +from postreise.plot.plot_sim_vs_hist import plot_generation_sim_vs_hist + +mock_plant = { + "plant_id": ["A", "B", "C", "D", "E", "F", "G", "H"], + "zone_id": [301, 302, 303, 304, 305, 306, 307, 308], + "Pmax": [100, 75, 150, 30, 50, 300, 200, 80], + "Pmin": [0, 0, 0, 0, 0, 100, 10, 0], + "type": ["solar", "wind", "hydro", "hydro", "wind", "coal", "ng", "wind"], + "zone_name": [ + "Far West", + "North", + "West", + "South", + "North Central", + "South Central", + "Coast", + "East", + ], +} + +mock_pg = pd.DataFrame( + { + "A": [80, 75, 72, 81], + "B": [22, 22, 25, 20], + "C": [130, 130, 130, 130], + "D": [25, 26, 27, 28], + "E": [10, 11, 9, 12], + "F": [290, 295, 295, 294], + "G": [190, 190, 191, 190], + "H": [61, 63, 65, 67], + }, + index=pd.date_range(start="2016-01-01", periods=4, freq="H"), +) + +grid_attrs = {"plant": mock_plant} +scenario = MockScenario(grid_attrs, pg=mock_pg) +scenario.info["interconnect"] = "Texas" +scenario.info["start_date"] = pd.Timestamp(2016, 1, 1) +scenario.info["end_date"] = pd.Timestamp(2016, 1, 1, 3) +scenario.state.grid.id2zone = { + k: v for k, v in zip(mock_plant["zone_id"], mock_plant["zone_name"]) +} +scenario.state.grid.zone2id = {v: k for k, v in scenario.state.grid.id2zone.items()} +scenario.state.grid.model = "usa_tamu" + +hist_gen = pd.DataFrame( + { + "solar": 100 * 4, + "wind": 205 * 4, + "hydro": 180 * 4, + "coal": 300 * 4, + "ng": 200 * 4, + }, + index=["Texas"], +) + + +def test_plot_generation_sim_vs_hist_argument_type(): + with pytest.raises(TypeError, match="hist_gen must be a pandas.DataFrame"): + plot_generation_sim_vs_hist(scenario, 3, "Texas") + with pytest.raises(TypeError, match="state must be a str"): + plot_generation_sim_vs_hist(scenario, pd.DataFrame(), ["Texas"]) + + +def test_plot_generation_sim_vs_hist_argument_value(): + with pytest.raises(ValueError, match="Invalid state"): + plot_generation_sim_vs_hist(scenario, hist_gen, "Washington") + + +def test_plot_generation_sim_vs_hist(): + plot_generation_sim_vs_hist( + scenario, hist_gen, "Texas", show_max=False, plot_show=False + ) + plot_generation_sim_vs_hist(scenario, hist_gen, "Texas", plot_show=False)