Source code for marmot.plottingmodules.hydro

# -*- coding: utf-8 -*-
"""Hydro generator plots.

This module creates hydro analysis plots.

DL: Oct 9th 2021, This plot is in need of work. 
It may not produce production ready figures.

@author: adyreson
"""

import datetime as dt
import logging
import os
from pathlib import Path
from typing import List

import matplotlib.ticker as mtick
import pandas as pd

import marmot.utils.mconfig as mconfig
from marmot.plottingmodules.plotutils.plot_data_helper import (
    GenCategories,
    PlotDataStoreAndProcessor,
)
from marmot.plottingmodules.plotutils.plot_exceptions import (
    DataSavedInModule,
    MissingInputData,
    MissingZoneData,
    UnderDevelopment,
)
from marmot.plottingmodules.plotutils.plot_library import SetupSubplot
from marmot.plottingmodules.plotutils.styles import GeneratorColorDict

logger = logging.getLogger("plotter." + __name__)
plot_data_settings: dict = mconfig.parser("plot_data")


[docs]class Hydro(PlotDataStoreAndProcessor): """Hydro generator plots. The hydro.py module contains methods that are related to hydro generators. Hydro inherits from the PlotDataStoreAndProcessor class to assist in creating figures. """ def __init__( self, Zones: List[str], Scenarios: List[str], AGG_BY: str, ordered_gen: List[str], marmot_solutions_folder: Path, gen_categories: GenCategories = GenCategories(), marmot_color_dict: dict = None, **kwargs, ): """ Args: Zones (List[str]): List of regions/zones to plot. Scenarios (List[str]): List of scenarios to plot. AGG_BY (str): Informs region type to aggregate by when creating plots. ordered_gen (List[str]): Ordered list of generator technologies to plot, order defines the generator technology position in stacked bar and area plots. marmot_solutions_folder (Path): Directory containing Marmot solution outputs. gen_categories (GenCategories): Instance of GenCategories class, groups generator technologies into defined categories. Deafults to GenCategories. marmot_color_dict (dict, optional): Dictionary of colors to use for generation technologies. Defaults to None. """ # Instantiation of PlotDataStoreAndProcessor super().__init__(AGG_BY, ordered_gen, marmot_solutions_folder, **kwargs) self.Zones = Zones self.Scenarios = Scenarios self.gen_categories = gen_categories if marmot_color_dict is None: self.marmot_color_dict = GeneratorColorDict.set_random_colors( self.ordered_gen ).color_dict else: self.marmot_color_dict = marmot_color_dict
[docs] def hydro_continent_net_load( self, start_date_range: str = None, end_date_range: str = None, **_ ): """Creates a scatter plot of hydro generation vs net load Data is saved within this method. Args: start_date_range (str, optional): Defines a start date at which to represent data from. Defaults to None. end_date_range (str, optional): Defines a end date at which to represent data to. Defaults to None. Returns: DataSavedInModule: DataSavedInModule exception """ return UnderDevelopment() outputs: dict = {} # List of properties needed by the plot, properties are a set of tuples and # contain 3 parts: required True/False, property name and scenarios required, # scenarios must be a list. properties = [(True, "generator_Generation", [self.Scenarios[0]])] # Runs get_formatted_data within PlotDataStoreAndProcessor to populate PlotDataStoreAndProcessor dictionary # with all required properties, returns a 1 if required data is missing check_input_data = self.get_formatted_data(properties) if 1 in check_input_data: return MissingInputData() for zone_input in self.Zones: # Location to save to hydro_figures = os.path.join(self.figure_folder, self.AGG_BY + "_Hydro") Stacked_Gen_read = self["generator_Generation"].get(self.Scenarios[0]) logger.info("Zone = " + zone_input) logger.info( "Winter is defined as date range: \ {} to {}".format( str(start_date_range), str(end_date_range) ) ) Net_Load = self.df_process_gen_inputs(Stacked_Gen_read) # Calculates Net Load by removing variable gen # Adjust list of values to drop depending on if it exists in Stacked_Gen df vre_gen_cat = [ name for name in self.gen_categories.vre if name in Net_Load.columns ] Net_Load = Net_Load.drop(labels=vre_gen_cat, axis=1) Net_Load = Net_Load.sum(axis=1) # Continent net load try: Stacked_Gen = Stacked_Gen_read.xs(zone_input, level=self.AGG_BY) except KeyError: logger.warning("No Generation in %s", zone_input) continue del Stacked_Gen_read Stacked_Gen = self.df_process_gen_inputs(Stacked_Gen) # Removes columns only containing 0 Stacked_Gen = Stacked_Gen.loc[:, (Stacked_Gen != 0).any(axis=0)] # end weekly loop try: Hydro_Gen = Stacked_Gen["Hydro"] except KeyError: logger.warning("No Hydro Generation in %s", zone_input) Hydro_Gen = MissingZoneData() continue del Stacked_Gen # Scatter plot by season mplt = SetupSubplot() fig, ax = mplt.get_figure() ax.scatter( Net_Load[end_date_range:start_date_range], Hydro_Gen[end_date_range:start_date_range], color="black", s=5, label="Non-winter", ) ax.scatter( Net_Load[start_date_range:], Hydro_Gen[start_date_range:], color="blue", s=5, label="Winter", alpha=0.5, ) ax.scatter( Net_Load[:end_date_range], Hydro_Gen[:end_date_range], color="blue", s=5, alpha=0.5, ) ax.set_ylabel( "In Region Hydro Generation (MW)", color="black", rotation="vertical" ) ax.set_xlabel( "Continent Net Load (MW)", color="black", rotation="horizontal" ) mplt.set_yaxis_major_tick_format() ax.xaxis.set_major_formatter(mtick.StrMethodFormatter("{x:,.0f}")) ax.margins(x=0.01) # Add title if plot_data_settings["plot_title_as_region"]: mplt.add_main_title(zone_input) mplt.add_legend(reverse_legend=True) fig.savefig( os.path.join( hydro_figures, zone_input + f"_Hydro_Versus_Continent_Net_Load_{self.Scenarios[0]}", ), dpi=600, bbox_inches="tight", ) outputs = DataSavedInModule() return outputs
[docs] def hydro_net_load(self, end: int = 7, timezone: str = "", **_): """Line plot of hydro generation vs net load. Creates separate plots for each week of the year, or longer depending on 'Day After' value passed through plot_select.csv Data is saved within this method. Args: end (float, optional): Determines length of plot period. Defaults to 7. timezone (str, optional): The timezone to display on the x-axes. Defaults to "". Returns: DataSavedInModule: DataSavedInModule exception """ outputs: dict = {} # List of properties needed by the plot, properties are a set of tuples and # contain 3 parts: required True/False, property name and scenarios required, # scenarios must be a list. properties = [(True, "generator_Generation", [self.Scenarios[0]])] # Runs get_formatted_data within PlotDataStoreAndProcessor to populate PlotDataStoreAndProcessor dictionary # with all required properties, returns a 1 if required data is missing check_input_data = self.get_formatted_data(properties) if 1 in check_input_data: return MissingInputData() for zone_input in self.Zones: logger.info("Zone = " + zone_input) # Location to save to hydro_figures = os.path.join(self.figure_folder, self.AGG_BY + "_Hydro") Stacked_Gen_read = self["generator_Generation"].get(self.Scenarios[0]) # The rest of the function won't work if this particular zone can't be found # in the solution file (e.g. if it doesn't include Mexico) try: Stacked_Gen = Stacked_Gen_read.xs(zone_input, level=self.AGG_BY) except KeyError: logger.warning("No Generation in %s", zone_input) continue del Stacked_Gen_read Stacked_Gen = self.df_process_gen_inputs(Stacked_Gen) # Calculates Net Load by removing variable gen # Adjust list of values to drop depending on if it exists in Stacked_Gen df vre_gen_cat = [ name for name in self.gen_categories.vre if name in Stacked_Gen.columns ] Net_Load = Stacked_Gen.drop(labels=vre_gen_cat, axis=1) Net_Load = Net_Load.sum(axis=1) # Removes columns that only contain 0 Stacked_Gen = Stacked_Gen.loc[:, (Stacked_Gen != 0).any(axis=0)] try: Hydro_Gen = Stacked_Gen["Hydro"] except KeyError: logger.warning("No Hydro Generation in %s", zone_input) Hydro_Gen = MissingZoneData() continue del Stacked_Gen first_date = Net_Load.index[0] # assumes weekly, could be something else if user changes end Marmot_plot_select for wk in range(1, 53): period_start = first_date + dt.timedelta(days=(wk - 1) * 7) period_end = period_start + dt.timedelta(days=end) logger.info(str(period_start) + " and next " + str(end) + " days.") Hydro_Period = Hydro_Gen[period_start:period_end] Net_Load_Period = Net_Load[period_start:period_end] # Data table of values to return to main program Data_Table_Out = pd.concat( [Net_Load_Period, Hydro_Period], axis=1, sort=False ) # Scatter plot by season mplt = SetupSubplot() fig, ax = mplt.get_figure() ax.plot( Hydro_Period, linewidth=2, color=self.marmot_color_dict.get("Hydro", "#333333"), label="Hydro", ) ax.plot(Net_Load_Period, color="black", label="Load") ax.set_ylabel("Generation (MW)", color="black", rotation="vertical") ax.set_xlabel(timezone, color="black", rotation="horizontal") mplt.set_yaxis_major_tick_format() ax.margins(x=0.01) mplt.set_subplot_timeseries_format() # Add title if plot_data_settings["plot_title_as_region"]: mplt.add_main_title(zone_input) # Add legend mplt.add_legend(reverse_legend=True) fig.savefig( os.path.join( hydro_figures, zone_input + f"_Hydro_And_Net_Load_{self.Scenarios[0]}_period_{str(wk)}", ), dpi=600, bbox_inches="tight", ) Data_Table_Out.to_csv( os.path.join( hydro_figures, zone_input + f"_Hydro_And_Net_Load_{self.Scenarios[0]}_period_{str(wk)}.csv", ) ) del fig del Data_Table_Out # end weekly loop # Scatter plot mplt = SetupSubplot() fig, ax = mplt.get_figure() ax.scatter(Net_Load, Hydro_Gen, color="black", s=5) ax.set_ylabel( "In-Region Hydro Generation (MW)", color="black", rotation="vertical" ) ax.set_xlabel( "In-Region Net Load (MW)", color="black", rotation="horizontal" ) mplt.set_yaxis_major_tick_format() ax.xaxis.set_major_formatter(mtick.StrMethodFormatter("{x:,.0f}")) ax.margins(x=0.01) mplt.add_legend(reverse_legend=True) if plot_data_settings["plot_title_as_region"]: mplt.add_main_title(zone_input) fig.savefig( os.path.join( hydro_figures, zone_input + f"_Hydro_Versus_Net_Load_{self.Scenarios[0]}", ), dpi=600, bbox_inches="tight", ) outputs = DataSavedInModule() return outputs